[MERGE]: Merged with trunk-server.
bzr revid: uco@tinyerp.com-20100617063537-8cv03xltm7fqw6rh
This commit is contained in:
+10
-2
@@ -28,6 +28,7 @@ import zipimport
|
||||
import osv
|
||||
import tools
|
||||
import tools.osutil
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import pooler
|
||||
|
||||
|
||||
@@ -663,6 +664,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
# TODO manage .csv file with noupdate == (kind == 'init')
|
||||
if ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
else:
|
||||
@@ -725,8 +729,12 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
load_demo_xml(cr, m, idref, mode)
|
||||
load_demo(cr, m, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
|
||||
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
# launch tests only in demo mode, as most tests will depend
|
||||
# on demo data. Other tests can be added into the regular
|
||||
# 'data' section, but should probably not alter the data,
|
||||
# as there is no rollback.
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
package_todo.append(package.name)
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
'base_menu.xml',
|
||||
'res/res_security.xml',
|
||||
'res/res_config.xml',
|
||||
'maintenance/maintenance_security.xml',
|
||||
'data/res.country.state.csv'
|
||||
],
|
||||
'update_xml': [
|
||||
@@ -58,7 +57,7 @@
|
||||
'res/partner/partner_wizard.xml',
|
||||
'res/bank_view.xml',
|
||||
'res/country_view.xml',
|
||||
'res/res_currency_view.xml',
|
||||
'res/res_currency_view.xml',
|
||||
'res/partner/crm_view.xml',
|
||||
'res/partner/wizard/partner_sms_send_view.xml',
|
||||
'res/partner/partner_data.xml',
|
||||
@@ -75,6 +74,7 @@
|
||||
],
|
||||
'test': [
|
||||
'test/base_test.xml',
|
||||
#'test/base_test.yml'
|
||||
'test/test_context.xml',
|
||||
'bug_lp541545.xml',
|
||||
],
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Groups">
|
||||
<field colspan="4" name="name" select="1"/>
|
||||
<field name="name" select="1"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Users">
|
||||
<field colspan="4" name="users" nolabel="1"/>
|
||||
@@ -78,18 +78,22 @@
|
||||
<form string="Users">
|
||||
<notebook colspan="4">
|
||||
<page string="Current Activity">
|
||||
<field name="company_id" widget="selection" readonly="0" context="{'user_preference':True}"/>
|
||||
<field name="company_id" widget="selection" readonly="0"
|
||||
context="{'user_id': self, 'user_preference': 1}"/>
|
||||
<field name="view" readonly="0"/>
|
||||
<newline/>
|
||||
<separator colspan="4" string="Preferences"/>
|
||||
</page>
|
||||
<page string="Preferences">
|
||||
<field name="password" password="True" readonly="0"/>
|
||||
<label colspan="4" string="Please note that you will have to logout and relog if you change your password."/>
|
||||
<field name="context_lang" completion="1" readonly="0"/>
|
||||
<field name="context_tz" completion="1" readonly="0" colspan="4"/>
|
||||
<field name="view" colspan="4" readonly="0" />
|
||||
<newline/>
|
||||
<field colspan="4" name="signature" readonly="0"/>
|
||||
<label string="" colspan="1"/>
|
||||
<label colspan="3" string="Please note that you will have to logout and relog if you change your password."/>
|
||||
<field name="context_lang" completion="1" readonly="0"/>
|
||||
<field name="context_tz" completion="1" readonly="0"/>
|
||||
<newline/>
|
||||
<separator string="Signature" colspan="4"/>
|
||||
<field colspan="4" name="signature" readonly="0" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
@@ -106,11 +110,12 @@
|
||||
<field name="active"/>
|
||||
<field name="login" select="1"/>
|
||||
<field name="password" password="True"/>
|
||||
<newline/>
|
||||
<notebook colspan="4">
|
||||
<page string="User">
|
||||
<field name="address_id" select="1"/>
|
||||
<field name="user_email" widget="email"/>
|
||||
<field name="company_id" required="1"/>
|
||||
<field name="company_id" required="1" context="{'user_id': self, 'user_preference': 1}"/>
|
||||
<field name="action_id" required="True"/>
|
||||
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
|
||||
<field name="context_lang"/>
|
||||
@@ -286,22 +291,22 @@
|
||||
<form position="attributes">
|
||||
<attribute name="string">Choose Your Interface</attribute>
|
||||
</form>
|
||||
<xpath expr="//label[@string='description']"
|
||||
position="attributes">
|
||||
<attribute name="string">Choose between the simplified interface and the extended one. We suggest you use the simplified interface. It has less options and fields but is easier to understand. You will be able to switch to the extended interface later.</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="title"]' position='attributes'>
|
||||
<attribute name='string'>Choose Your Interface</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||
<attribute name='string'></attribute>
|
||||
<attribute name='rowspan'>12</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//label[@string='description']"
|
||||
position="attributes">
|
||||
<attribute name="string">Choose between the simplified interface and the extended one. We suggest you use the simplified interface. It has less options and fields but is easier to understand. You will be able to switch to the extended interface later.</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="title"]' position='attributes'>
|
||||
<attribute name='string'>Choose Your Interface</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||
<attribute name='string'></attribute>
|
||||
<attribute name='rowspan'>12</attribute>
|
||||
</xpath>
|
||||
<group string="res_config_contents" position="replace">
|
||||
<group colspan="4">
|
||||
<field colspan="4" name="view" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group colspan="4">
|
||||
<field colspan="4" name="view" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<xpath expr='//button[@name="action_skip"]' position='replace'/>
|
||||
<xpath expr='//button[@name="action_next"]' position='attributes'>
|
||||
<attribute name='string'>Set</attribute>
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-01-04 06:02+0000\n"
|
||||
"Last-Translator: Kuvaly [LCT] <kuvaly@seznam.cz>\n"
|
||||
"PO-Revision-Date: 2010-06-06 04:19+0000\n"
|
||||
"Last-Translator: Vladimír Burian <vladimir.burian@email.cz>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-01 03:39+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-07 03:42+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -3880,7 +3880,7 @@ msgstr "Kontakty"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.fo
|
||||
msgid "Faroe Islands"
|
||||
msgstr "Farské ostrovy"
|
||||
msgstr "Faerské ostrovy"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.wizard,name:base.wizard_upgrade
|
||||
|
||||
@@ -8,14 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-31 04:09+0000\n"
|
||||
"Last-Translator: Sharoon Thomas http://openlabs.co.in "
|
||||
"<sharoonthomas@teagarden.in>\n"
|
||||
"PO-Revision-Date: 2010-06-07 04:17+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-01 03:43+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-08 03:40+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -493,7 +492,7 @@ msgstr "Model Description"
|
||||
#. module: base
|
||||
#: field:workflow.transition,trigger_expr_id:0
|
||||
msgid "Trigger Expression"
|
||||
msgstr ""
|
||||
msgstr "Trigger Expression"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.jo
|
||||
@@ -833,7 +832,7 @@ msgstr "S. Georgia & S. Sandwich Isls."
|
||||
#. module: base
|
||||
#: field:ir.actions.url,url:0
|
||||
msgid "Action URL"
|
||||
msgstr ""
|
||||
msgstr "Action URL"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
@@ -1050,7 +1049,7 @@ msgstr "East Timor"
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Simple domain setup"
|
||||
msgstr ""
|
||||
msgstr "Simple domain setup"
|
||||
|
||||
#. module: base
|
||||
#: field:res.currency,accuracy:0
|
||||
@@ -1101,7 +1100,7 @@ msgstr "ir.rule"
|
||||
#. module: base
|
||||
#: selection:ir.cron,interval_type:0
|
||||
msgid "Days"
|
||||
msgstr ""
|
||||
msgstr "Days"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.report.custom.fields,width:0
|
||||
@@ -1436,27 +1435,27 @@ msgstr "STOCK_SELECT_COLOR"
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
msgid "STOCK_NO"
|
||||
msgstr ""
|
||||
msgstr "STOCK_NO"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.st
|
||||
msgid "Saint Tome (Sao Tome) and Principe"
|
||||
msgstr ""
|
||||
msgstr "Saint Tome (Sao Tome) and Principe"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.partner.address,type:0
|
||||
msgid "Invoice"
|
||||
msgstr ""
|
||||
msgstr "Invoice"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
msgid "STOCK_REDO"
|
||||
msgstr ""
|
||||
msgstr "STOCK_REDO"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bb
|
||||
msgid "Barbados"
|
||||
msgstr ""
|
||||
msgstr "Barbados"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mg
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-01 05:21+0000\n"
|
||||
"Last-Translator: goranc <goranc@gmail.com>\n"
|
||||
"PO-Revision-Date: 2010-06-16 04:09+0000\n"
|
||||
"Last-Translator: Goran Kliska <gkliska@gmail.com>\n"
|
||||
"Language-Team: openerp-translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-02 03:32+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-17 03:32+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
@@ -1381,7 +1381,7 @@ msgstr "Gruzija"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.pl
|
||||
msgid "Poland"
|
||||
msgstr ""
|
||||
msgstr "Poljska"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.module.module,state:0
|
||||
@@ -2703,7 +2703,7 @@ msgstr ""
|
||||
#: help:ir.values,key2:0
|
||||
msgid ""
|
||||
"The kind of action or button in the client side that will trigger the action."
|
||||
msgstr ""
|
||||
msgstr "Vrsta akcije ili gumba na klijentskoj strani koja će okinuti akciju."
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
|
||||
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-01 05:19+0000\n"
|
||||
"PO-Revision-Date: 2010-06-16 04:11+0000\n"
|
||||
"Last-Translator: Harry (Open ERP) <hmo@tinyerp.com>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-02 03:32+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-17 03:32+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-03-17 05:16+0000\n"
|
||||
"Last-Translator: Valentin Caragea <carageav@gmail.com>\n"
|
||||
"PO-Revision-Date: 2010-06-07 04:16+0000\n"
|
||||
"Last-Translator: filsys <office@filsystem.ro>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-01 03:42+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-08 03:39+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -774,12 +774,12 @@ msgstr "Uganda"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ne
|
||||
msgid "Niger"
|
||||
msgstr ""
|
||||
msgstr "Niger"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ba
|
||||
msgid "Bosnia-Herzegovina"
|
||||
msgstr ""
|
||||
msgstr "Bosnia-Herțegovina"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.report.custom.fields,alignment:0
|
||||
@@ -789,7 +789,7 @@ msgstr "Aliniere"
|
||||
#. module: base
|
||||
#: selection:ir.rule,operator:0
|
||||
msgid ">="
|
||||
msgstr ""
|
||||
msgstr ">="
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -813,12 +813,12 @@ msgstr ""
|
||||
#: field:ir.module.module,website:0
|
||||
#: field:res.partner,website:0
|
||||
msgid "Website"
|
||||
msgstr ""
|
||||
msgstr "Website"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.rule.group,rules:0
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
msgstr "Teste"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.repository:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-01 05:22+0000\n"
|
||||
"PO-Revision-Date: 2010-06-16 04:12+0000\n"
|
||||
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\n"
|
||||
"Language-Team: <>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-02 03:32+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-17 03:32+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-01 05:22+0000\n"
|
||||
"PO-Revision-Date: 2010-06-16 04:09+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-02 03:32+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-17 03:32+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-01 05:19+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"PO-Revision-Date: 2010-06-16 04:09+0000\n"
|
||||
"Last-Translator: Eleanor Chen <chenyueg@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-02 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-17 03:33+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -3156,7 +3156,7 @@ msgstr "网页"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "English (CA)"
|
||||
msgstr "英语(加拿大)"
|
||||
msgstr "英语(加拿大)"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.event,planned_revenue:0
|
||||
@@ -6656,7 +6656,7 @@ msgstr "职能名称"
|
||||
#. module: base
|
||||
#: view:maintenance.contract.wizard:0
|
||||
msgid "_Cancel"
|
||||
msgstr "取消(_C)"
|
||||
msgstr "取消(_C)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cy
|
||||
|
||||
@@ -1244,11 +1244,6 @@
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Record rules">
|
||||
<separator colspan="4" string="Combination of rules"/>
|
||||
<label align="0.0" angle="2" colspan="4" string="1. Global rules are combined together with a logical AND operator, and with the result of the following steps"/>
|
||||
<label align="0.0" angle="2" colspan="4" string="2. Group-specific rules are combined together with a logical AND operator"/>
|
||||
<label align="0.0" angle="2" colspan="4" string="3. If user belongs to several groups, the results from step 2 are combined with logical OR operator"/>
|
||||
<label align="0.0" angle="2" colspan="4" string="Example: GLOBAL_RULE_1 AND GLOBAL_RULE_2 AND ( (GROUP_A_RULE_1 AND GROUP_A_RULE_2) OR (GROUP_B_RULE_1 AND GROUP_B_RULE_2) )"/>
|
||||
<group col="2" colspan="2">
|
||||
<separator colspan="2" string="General"/>
|
||||
<field colspan="4" name="name"/>
|
||||
@@ -1269,7 +1264,16 @@
|
||||
<group colspan="4" expand="1">
|
||||
<field name="global" select="1"/>
|
||||
<field name="groups" select="1" nolabel="1" colspan="4"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator colspan="4" string="Combination of rules"/>
|
||||
<label align="0.0" colspan="4"
|
||||
string="1. Global rules are combined together with a logical AND operator, and with the result of the following steps"/>
|
||||
<label align="0.0" colspan="4"
|
||||
string="2. Group-specific rules are combined together with a logical AND operator"/>
|
||||
<label align="0.0" colspan="4"
|
||||
string="3. If user belongs to several groups, the results from step 2 are combined with logical OR operator"/>
|
||||
<label align="0.0" colspan="4"
|
||||
string="Example: GLOBAL_RULE_1 AND GLOBAL_RULE_2 AND ( (GROUP_A_RULE_1 AND GROUP_A_RULE_2) OR (GROUP_B_RULE_1 AND GROUP_B_RULE_2) )"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
import time
|
||||
from tools.config import config
|
||||
@@ -564,7 +565,7 @@ class actions_server(osv.osv):
|
||||
'object':obj,
|
||||
'obj': obj,
|
||||
}
|
||||
exec action.code in localdict
|
||||
eval(action.code, localdict, "exec")
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
else:
|
||||
|
||||
@@ -25,6 +25,7 @@ from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import netsvc
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import pooler
|
||||
from osv import fields, osv
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from osv.orm import browse_null
|
||||
import ir
|
||||
import report.custom
|
||||
from tools.translate import _
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import netsvc
|
||||
|
||||
class report_custom(osv.osv):
|
||||
|
||||
@@ -21,10 +21,14 @@
|
||||
|
||||
from osv import fields,osv
|
||||
import time
|
||||
from operator import itemgetter
|
||||
from functools import partial
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class ir_rule(osv.osv):
|
||||
_name = 'ir.rule'
|
||||
_MODES = ['read', 'write', 'create', 'unlink']
|
||||
|
||||
def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}):
|
||||
res = {}
|
||||
@@ -44,13 +48,7 @@ class ir_rule(osv.osv):
|
||||
return res
|
||||
|
||||
def _check_model_obj(self, cr, uid, ids, context={}):
|
||||
model_obj = self.pool.get('ir.model')
|
||||
for rule in self.browse(cr, uid, ids, context):
|
||||
model = model_obj.browse(cr, uid, rule.model_id.id, context).model
|
||||
obj = self.pool.get(model)
|
||||
if isinstance(obj, osv.osv_memory):
|
||||
return False
|
||||
return True
|
||||
return not any(isinstance(self.pool.get(rule.model_id.model), osv.osv_memory) for rule in self.browse(cr, uid, ids, context))
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=128, select=1),
|
||||
@@ -89,6 +87,8 @@ class ir_rule(osv.osv):
|
||||
|
||||
@tools.cache()
|
||||
def _compute_domain(self, cr, uid, model_name, mode="read"):
|
||||
if mode not in self._MODES:
|
||||
raise ValueError('Invalid mode: %r' % (mode,))
|
||||
group_rule = {}
|
||||
global_rules = []
|
||||
|
||||
@@ -114,6 +114,24 @@ class ir_rule(osv.osv):
|
||||
dom += self.domain_create(cr, uid, value)
|
||||
return dom
|
||||
|
||||
def clear_cache(self, cr, uid):
|
||||
cr.execute("""SELECT DISTINCT m.model
|
||||
FROM ir_rule r
|
||||
JOIN ir_model m
|
||||
ON r.model_id = m.id
|
||||
WHERE r.global
|
||||
OR EXISTS (SELECT 1
|
||||
FROM rule_group_rel g_rel
|
||||
JOIN res_groups_users_rel u_rel
|
||||
ON g_rel.group_id = u_rel.gid
|
||||
WHERE g_rel.rule_group_id = r.id
|
||||
AND u_rel.uid = %s)
|
||||
""", (uid,))
|
||||
models = map(itemgetter(0), cr.fetchall())
|
||||
clear = partial(self._compute_domain.clear_cache, cr.dbname, uid)
|
||||
[clear(model, mode) for model in models for mode in self._MODES]
|
||||
|
||||
|
||||
def domain_get(self, cr, uid, model_name, mode='read', context={}):
|
||||
dom = self._compute_domain(cr, uid, model_name, mode=mode)
|
||||
if dom:
|
||||
|
||||
@@ -68,15 +68,15 @@ class ir_ui_menu(osv.osv):
|
||||
self._cache = {}
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
context=None, count=False):
|
||||
|
||||
super_offset = cond(uid == 1, offset, 0)
|
||||
super_limit = cond(uid == 1, limit, None)
|
||||
super_count = cond(uid == 1, count, False)
|
||||
|
||||
ids = super(ir_ui_menu, self).search(cr, uid, args, super_offset,
|
||||
super_limit, order,
|
||||
context=context, count=super_count)
|
||||
super_limit, order, context=context, count=super_count)
|
||||
|
||||
if uid == 1:
|
||||
return ids
|
||||
|
||||
@@ -104,9 +104,9 @@ class ir_ui_menu(osv.osv):
|
||||
restrict_to_groups = [g.id for g in menu.groups_id]
|
||||
if not user_groups.intersection(restrict_to_groups):
|
||||
continue
|
||||
result.append(menu.id)
|
||||
self._cache[key] = True
|
||||
continue
|
||||
#result.append(menu.id)
|
||||
#self._cache[key] = True
|
||||
#continue
|
||||
|
||||
if menu.action:
|
||||
# we check if the user has access to the action of the menu
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields,osv
|
||||
from lxml import etree
|
||||
from tools import graph
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
import netsvc
|
||||
import os
|
||||
@@ -70,7 +71,7 @@ class view(osv.osv):
|
||||
method=True),
|
||||
}
|
||||
_defaults = {
|
||||
'arch': '<?xml version="1.0"?>\n<tree string="Unknwown">\n\t<field name="name"/>\n</tree>',
|
||||
'arch': '<?xml version="1.0"?>\n<tree string="My view">\n\t<field name="name"/>\n</tree>',
|
||||
'priority': 16
|
||||
}
|
||||
_order = "priority"
|
||||
|
||||
@@ -233,7 +233,7 @@ class ir_values(osv.osv):
|
||||
pos+=1
|
||||
try:
|
||||
datas = self.pool.get(model).read(cr, uid, [id], fields, context)
|
||||
except except_orm:
|
||||
except except_orm, e:
|
||||
return False
|
||||
datas= datas and datas[0] or None
|
||||
if not datas:
|
||||
@@ -260,9 +260,6 @@ class ir_values(osv.osv):
|
||||
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 !!!')
|
||||
# else:
|
||||
# #raise osv.except_osv('Error !','You have not permission to perform operation !!!')
|
||||
# res2.remove(r)
|
||||
return res2
|
||||
ir_values()
|
||||
|
||||
|
||||
@@ -2,13 +2,5 @@
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
|
||||
<record model="res.groups" id="group_maintenance_manager">
|
||||
<field name="name">Maintenance Manager</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_maintenance_contract">
|
||||
<field eval="[(6,0,[ref('group_maintenance_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -25,6 +25,7 @@ import re
|
||||
import urllib
|
||||
import os
|
||||
import imp
|
||||
import logging
|
||||
import tools
|
||||
from osv import fields, osv, orm
|
||||
import zipfile
|
||||
@@ -445,17 +446,17 @@ class module(osv.osv):
|
||||
tools.trans_load(cr.dbname, f, lang, verbose=False)
|
||||
|
||||
def check(self, cr, uid, ids, context=None):
|
||||
logger = netsvc.Logger()
|
||||
logger = logging.getLogger('init')
|
||||
for mod in self.browse(cr, uid, ids, context=context):
|
||||
if not mod.description:
|
||||
logger.notifyChannel("init", netsvc.LOG_WARNING, 'module %s: description is empty !' % (mod.name,))
|
||||
logger.warn('module %s: description is empty !', mod.name)
|
||||
|
||||
if not mod.certificate or not mod.certificate.isdigit():
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: no quality certificate' % (mod.name,))
|
||||
logger.info('module %s: no quality certificate', mod.name)
|
||||
else:
|
||||
val = long(mod.certificate[2:]) % 97 == 29
|
||||
if not val:
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module %s: invalid quality certificate: %s' % (mod.name, mod.certificate))
|
||||
logger.critical('module %s: invalid quality certificate: %s', mod.name, mod.certificate)
|
||||
raise osv.except_osv(_('Error'), _('Module %s: Invalid Quality Certificate') % (mod.name,))
|
||||
|
||||
|
||||
|
||||
@@ -29,13 +29,6 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_module_category_tree" model="ir.actions.act_window">
|
||||
<field name="name">Categories of Modules</field>
|
||||
<field name="res_model">ir.module.category</field>
|
||||
<field name="view_type">tree</field>
|
||||
<field name="domain">[('parent_id','=',False)]</field>
|
||||
</record>
|
||||
<menuitem action="action_module_category_tree" id="menu_action_module_category_tree" parent="base.menu_management"/>
|
||||
|
||||
<!-- Click on a category -->
|
||||
|
||||
@@ -46,17 +39,22 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search modules">
|
||||
<group col='10' colspan='4'>
|
||||
<filter icon="terp-sale" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
|
||||
<filter icon="terp-sale" string="Uninstalled" domain="[('state', 'in', ['uninstalled', 'uninstallable'])]"/>
|
||||
<filter icon="terp-sale" string="To be upgraded" domain="[('state','in', ['to upgrade', 'to remove', 'to install'])]"/>
|
||||
<filter icon="terp-check" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
|
||||
<filter icon="terp-dialog-close" string="Uninstalled" domain="[('state', 'in', ['uninstalled', 'uninstallable'])]"/>
|
||||
<filter icon="terp-gtk-jump-to-ltr" string="To be upgraded" domain="[('state','in', ['to upgrade', 'to remove', 'to install'])]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-sale" string="Certified" domain="[('certificate','<>', False)]"/>
|
||||
<filter icon="terp-camera_test" string="Certified" domain="[('certificate','<>', False)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="state" readonly="1" select="1"/>
|
||||
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group expand="0" string="Group By..." colspan="11" col="11" groups="base.group_extended">
|
||||
<filter string="Category" icon="terp-personal" domain="[]" context="{'group_by':'category_id'}"/>
|
||||
<filter string="Author" icon="terp-personal" domain="[]" context="{'group_by':'author'}"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
@@ -160,7 +158,7 @@
|
||||
<field name="domain"/>
|
||||
<field name="search_view_id" ref="view_module_filter"/>
|
||||
</record>
|
||||
<menuitem action="open_module_tree" id="menu_module_tree" parent="base.menu_management"/>
|
||||
<menuitem action="open_module_tree" id="menu_module_tree" parent="base.menu_management"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -50,7 +50,8 @@ class wizard_update_module(wizard.interface):
|
||||
return {'update': update, 'add': add}
|
||||
|
||||
def _action_module_open(self, cr, uid, data, context):
|
||||
return {
|
||||
|
||||
res = {
|
||||
'domain': str([]),
|
||||
'name': 'Module List',
|
||||
'view_type': 'form',
|
||||
@@ -59,8 +60,11 @@ class wizard_update_module(wizard.interface):
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window'
|
||||
}
|
||||
|
||||
|
||||
|
||||
search_view_id = pooler.get_pool(cr.dbname).get('ir.ui.view').search(cr, uid, [('name','=','ir.module.module.list.select')], context=context)
|
||||
if search_view_id:
|
||||
res.update({'search_view_id' : search_view_id[0]})
|
||||
return res
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
|
||||
@@ -63,6 +63,7 @@ class ir_property(osv.osv):
|
||||
|
||||
'type' : fields.selection([('char', 'Char'),
|
||||
('float', 'Float'),
|
||||
('boolean', 'Boolean'),
|
||||
('integer', 'Integer'),
|
||||
('integer_big', 'Integer Big'),
|
||||
('text', 'Text'),
|
||||
@@ -84,10 +85,20 @@ class ir_property(osv.osv):
|
||||
value = values.pop('value', None)
|
||||
if not value:
|
||||
return values
|
||||
type_ = values.get('type', 'many2one')
|
||||
|
||||
prop = None
|
||||
type_ = values.get('type')
|
||||
if not type_:
|
||||
if ids:
|
||||
prop = self.browse(cr, uid, ids[0])
|
||||
type_ = prop.type
|
||||
else:
|
||||
type_ = self._defaults['type']
|
||||
|
||||
type2field = {
|
||||
'char': 'value_text',
|
||||
'float': 'value_float',
|
||||
'boolean' : 'value_integer',
|
||||
'integer': 'value_integer',
|
||||
'integer_big': 'value_integer',
|
||||
'text': 'value_text',
|
||||
@@ -106,9 +117,9 @@ class ir_property(osv.osv):
|
||||
elif isinstance(value, (int, long)):
|
||||
field_id = values.get('fields_id')
|
||||
if not field_id:
|
||||
if not ids:
|
||||
if not prop:
|
||||
raise ValueError()
|
||||
field_id = self.browse(cr, uid, ids[0]).fields_id
|
||||
field_id = prop.fields_id
|
||||
else:
|
||||
field_id = self.pool.get('ir.model.fields').browse(cr, uid, field_id)
|
||||
|
||||
@@ -137,6 +148,8 @@ class ir_property(osv.osv):
|
||||
return record.value_text
|
||||
elif record.type == 'float':
|
||||
return record.value_float
|
||||
elif record.type == 'boolean':
|
||||
return bool(record.value_integer)
|
||||
elif record.type in ('integer', 'integer_big'):
|
||||
return record.value_integer
|
||||
elif record.type == 'binary':
|
||||
@@ -146,6 +159,8 @@ class ir_property(osv.osv):
|
||||
elif record.type == 'datetime':
|
||||
return record.value_datetime
|
||||
elif record.type == 'date':
|
||||
if not record.value_datetime:
|
||||
return False
|
||||
return time.strftime('%Y-%m-%d', time.strptime(record.value_datetime, '%Y-%m-%d %H:%M:%S'))
|
||||
|
||||
return False
|
||||
@@ -158,18 +173,27 @@ class ir_property(osv.osv):
|
||||
return self.get_by_id(cr, uid, nid, context=context)
|
||||
return False
|
||||
|
||||
def _get_domain_default(self, cr, uid, prop_name, model, context=None):
|
||||
domain = self._get_domain(cr, uid, prop_name, model, context=context)
|
||||
if domain is None:
|
||||
return None
|
||||
return ['&', ('res_id', '=', False)] + domain
|
||||
|
||||
def _get_domain(self, cr, uid, prop_name, model, context=None):
|
||||
cr.execute('select id from ir_model_fields where name=%s and model=%s', (prop_name, model))
|
||||
res = cr.fetchone()
|
||||
if not res:
|
||||
return None
|
||||
|
||||
company = self.pool.get('res.company')
|
||||
cid = company._company_default_get(cr, uid, model, res[0],
|
||||
if 'force_company' in context and context['force_company']:
|
||||
cid = context['force_company']
|
||||
else:
|
||||
company = self.pool.get('res.company')
|
||||
cid = company._company_default_get(cr, uid, model, res[0],
|
||||
context=context)
|
||||
|
||||
domain = ['&', ('fields_id', '=', res[0]),
|
||||
'|', ('company_id', '=', cid), ('company_id', '=', False)]
|
||||
'|', ('company_id', '=', cid), ('company_id', '=', False)]
|
||||
return domain
|
||||
|
||||
ir_property()
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<field name="company_id" select="1" groups="base.group_multi_company"/>
|
||||
<field colspan="4" name="fields_id" select="1"/>
|
||||
<field colspan="4" name="type"/>
|
||||
<group colspan="4" attrs="{'invisible' : [('type', 'not in', ('integer', 'integer_big'))]}">
|
||||
<group colspan="4" attrs="{'invisible' : [('type', 'not in', ('integer', 'integer_big', 'boolean'))]}">
|
||||
<field colspan="4" name="value_integer" widget="integer"/>
|
||||
</group>
|
||||
<group colspan="4" attrs="{'invisible' : [('type', '!=', 'float')]}">
|
||||
|
||||
@@ -159,6 +159,9 @@ class res_partner(osv.osv):
|
||||
default.update({'name': name+ _(' (copy)'), 'events':[]})
|
||||
return super(res_partner, self).copy(cr, uid, id, default, context)
|
||||
|
||||
def do_share(self, cr, uid, ids, *args):
|
||||
return True
|
||||
|
||||
def _check_ean_key(self, cr, uid, ids):
|
||||
for partner_o in pooler.get_pool(cr.dbname).get('res.partner').read(cr, uid, ids, ['ean13',]):
|
||||
thisean=partner_o['ean13']
|
||||
@@ -275,7 +278,7 @@ class res_partner_address(osv.osv):
|
||||
_description ='Partner Addresses'
|
||||
_name = 'res.partner.address'
|
||||
_columns = {
|
||||
'partner_id': fields.many2one('res.partner', 'Company Name', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner Name', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
|
||||
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
|
||||
'function': fields.char('Function', size=64),
|
||||
'title': fields.selection(_contact_title_get, 'Title', size=32),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<menuitem id="menu_address_book" name="Address Book" parent="menu_base_partner" sequence="2"/>
|
||||
|
||||
<menuitem id="menu_base_config" name="Configuration" parent="menu_base_partner" sequence="9"
|
||||
<menuitem id="menu_base_config" name="Configuration" parent="menu_base_partner" sequence="11"
|
||||
groups="group_system"/>
|
||||
|
||||
<menuitem id="menu_config_address_book" name="Address Book" parent="menu_base_config" sequence="3"
|
||||
@@ -27,18 +27,18 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Contact">
|
||||
<group>
|
||||
<filter string="Customer" name="customer" icon="terp-crm" domain="[('is_customer_add','=','1')]" />
|
||||
<filter string="Supplier" name="supplier" icon="terp-crm" domain="[('is_supplier_add','=','1')]" />
|
||||
<filter string="Customer" name="customer" icon="terp-personal" domain="[('is_customer_add','=','1')]" />
|
||||
<filter string="Supplier" name="supplier" icon="terp-personal" domain="[('is_supplier_add','=','1')]" />
|
||||
<separator orientation="vertical"/>
|
||||
<field name="partner_id" />
|
||||
<field name="name" />
|
||||
<field name="country_id" />
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Partner" icon="terp-crm" domain="[]" context="{'group_by' : 'partner_id'}" />
|
||||
<filter string="Country" icon="terp-crm" domain="[]" context="{'group_by' : 'country_id'}" />
|
||||
<filter string="Type" icon="terp-crm" domain="[]" context="{'group_by' : 'type'}" />
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Partner" icon="terp-personal" domain="[]" context="{'group_by' : 'partner_id'}" />
|
||||
<filter string="Country" icon="terp-go-home" domain="[]" context="{'group_by' : 'country_id'}" />
|
||||
<filter string="Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -259,7 +259,7 @@
|
||||
<group colspan="1" col="2">
|
||||
<field name="customer" select="1"/>
|
||||
<field name="supplier"/>
|
||||
<field name="employee"/>
|
||||
<!-- <field name="employee"/>-->
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
@@ -317,9 +317,9 @@
|
||||
<page string="Notes">
|
||||
<field colspan="4" name="comment" nolabel="1"/>
|
||||
</page>
|
||||
<page string="History">
|
||||
<field colspan="4" name="events" nolabel="1" widget="one2many_list" string="Event Logs"/>
|
||||
</page>
|
||||
<!-- <page string="History">-->
|
||||
<!-- <field colspan="4" name="events" nolabel="1" widget="one2many_list" string="Event Logs"/>-->
|
||||
<!-- </page>-->
|
||||
|
||||
</notebook>
|
||||
</form>
|
||||
@@ -333,20 +333,20 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Partner">
|
||||
<group col='10' colspan='4'>
|
||||
<filter string="Customers" icon="terp-partner" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" icon="terp-partner" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<filter string="Customers" name="customer" icon="terp-personal" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" name="supplier" icon="terp-personal" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="address" select="1"/>
|
||||
<field name="country" select="1"/>
|
||||
<field name="category_id" select="1"/>
|
||||
<field name="user_id" select="1">
|
||||
<filter string="My Partners" icon="terp-partner" domain="[('user_id','=',uid)]"/>
|
||||
<filter string="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
|
||||
</field>
|
||||
</group>
|
||||
<newline />
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Salesman" icon="terp-crm" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Salesman" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -357,7 +357,7 @@
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="context">{"search_default_supplier":supplier}</field>
|
||||
<field name="context">{"search_default_customer":1}</field>
|
||||
<field name="search_view_id" ref="view_res_partner_filter"/>
|
||||
</record>
|
||||
<record id="action_partner_form_view1" model="ir.actions.act_window.view">
|
||||
@@ -384,7 +384,7 @@
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('customer','=',1)]</field>
|
||||
<field name="context">{'default_customer':1}</field>
|
||||
<field name="context">{'default_customer':1, 'search_default_customer':1}</field>
|
||||
<field name="filter" eval="True"/>
|
||||
</record>
|
||||
|
||||
@@ -417,15 +417,6 @@
|
||||
<field name="filter" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="action_partner_customer_form_new" model="ir.actions.act_window">
|
||||
<field name="name">New Partner</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form,tree</field>
|
||||
<field name="view_id" ref="view_partner_form"/>
|
||||
</record>
|
||||
|
||||
<record id="view_payterm_form" model="ir.ui.view">
|
||||
<field name="name">res.payterm</field>
|
||||
<field name="model">res.payterm</field>
|
||||
@@ -549,6 +540,7 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!--
|
||||
<record id="action_partner_category" model="ir.actions.act_window">
|
||||
<field name="name">Partners by Categories</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -559,6 +551,7 @@
|
||||
</record>
|
||||
<menuitem action="action_partner_category" id="menu_partner_category_main" parent="base.menu_address_book" sequence="1"
|
||||
groups="base.group_extended"/>
|
||||
-->
|
||||
|
||||
<record id="action_partner_by_category" model="ir.actions.act_window">
|
||||
<field name="name">Partner Categories</field>
|
||||
|
||||
@@ -24,6 +24,7 @@ from osv import fields
|
||||
import os
|
||||
import tools
|
||||
from tools.translate import _
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class multi_company_default(osv.osv):
|
||||
"""
|
||||
@@ -91,11 +92,18 @@ class res_company(osv.osv):
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
|
||||
user_preference = context and context.get('user_preference', False) or False
|
||||
if context is None:
|
||||
context = {}
|
||||
user_preference = context.get('user_preference', False)
|
||||
if user_preference:
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids]))
|
||||
# TODO: improve this as soon as the client sends the proper
|
||||
# combination of active_id and active_model we'll be able to
|
||||
# use active_id here to restrict to the user being modified instead
|
||||
# of current user.
|
||||
user_id = context.get('user_id', uid)
|
||||
|
||||
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)
|
||||
@@ -125,12 +133,12 @@ class res_company(osv.osv):
|
||||
ids = self._get_company_children(cr, uid, company)
|
||||
return ids
|
||||
|
||||
@tools.cache()
|
||||
def _get_company_children(self, cr, uid=None, company=None):
|
||||
if not company:
|
||||
return []
|
||||
ids = self.search(cr, uid, [('parent_id','child_of',[company])])
|
||||
return ids
|
||||
_get_company_children = tools.cache()(_get_company_children)
|
||||
|
||||
def _get_partner_hierarchy(self, cr, uid, company_id, context={}):
|
||||
if company_id:
|
||||
@@ -160,7 +168,6 @@ class res_company(osv.osv):
|
||||
|
||||
def write(self, cr, *args, **argv):
|
||||
self.cache_restart(cr)
|
||||
# Restart the cache on the company_get method
|
||||
return super(res_company, self).write(cr, *args, **argv)
|
||||
|
||||
def _get_euro(self, cr, uid, context={}):
|
||||
|
||||
@@ -390,6 +390,8 @@ class res_config_installer(osv.osv_memory):
|
||||
cr, uid, fields, context, read_access)
|
||||
|
||||
for module in self._already_installed(cr, uid, context=context):
|
||||
if module.name not in fields:
|
||||
continue
|
||||
fields[module.name].update(
|
||||
readonly=True,
|
||||
help=fields[module.name].get('help', '') +
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields, osv
|
||||
from locale import localeconv
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class lang(osv.osv):
|
||||
_name = "res.lang"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Logs">
|
||||
<filter icon="terp-sale" string="My Logs" domain="[('user_id','=',uid)]" name="my"/>
|
||||
<filter icon="terp-stock_align_left_24" string="My Logs" domain="[('user_id','=',uid)]" name="my"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
<field name="name">Partner Manager</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_base_partner">
|
||||
<field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_base_config">
|
||||
<field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from osv.orm import except_orm, browse_record
|
||||
from osv.orm import browse_record
|
||||
import tools
|
||||
import operator
|
||||
from functools import partial
|
||||
import pytz
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
@@ -55,7 +55,6 @@ class groups(osv.osv):
|
||||
raise osv.except_osv(_('Error'),
|
||||
_('The name of the group can not start with "-"'))
|
||||
res = super(groups, self).write(cr, uid, ids, vals, context=context)
|
||||
# Restart the cache on the company_get method
|
||||
self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
|
||||
return res
|
||||
|
||||
@@ -233,8 +232,13 @@ class users(osv.osv):
|
||||
'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'),
|
||||
'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'),
|
||||
'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'),
|
||||
|
||||
# Special behavior for this field: res.company.search() will only return the companies
|
||||
# available to the current user (should be the user's companies?), when the user_preference
|
||||
# context is set.
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True,
|
||||
help="The company this user is currently working for."),
|
||||
help="The company this user is currently working for.", context={'user_preference': True}),
|
||||
|
||||
'company_ids':fields.many2many('res.company','res_company_users_rel','user_id','cid','Companies'),
|
||||
'context_lang': fields.selection(_lang_get, 'Language', required=True,
|
||||
help="Sets the language for the user's user interface, when UI "
|
||||
@@ -277,7 +281,7 @@ class users(osv.osv):
|
||||
return all(((this.company_id in this.company_ids) or not this.company_ids) for this in self.browse(cr, uid, ids, context))
|
||||
|
||||
_constraints = [
|
||||
(_check_company, 'The chosen company is not in the allowed companies', ['company_id', 'company_ids']),
|
||||
(_check_company, 'The chosen company is not in the allowed companies for this user', ['company_id', 'company_ids']),
|
||||
]
|
||||
|
||||
_sql_constraints = [
|
||||
@@ -327,22 +331,28 @@ class users(osv.osv):
|
||||
'groups_id': _get_group,
|
||||
'address_id': False,
|
||||
}
|
||||
def company_get(self, cr, uid, uid2, context={}):
|
||||
return self._get_company(cr, uid, context=context, uid2=uid2)
|
||||
company_get = tools.cache()(company_get)
|
||||
|
||||
def write(self, cr, uid, ids, values, *args, **argv):
|
||||
if (ids == [uid]):
|
||||
ok = True
|
||||
for k in values.keys():
|
||||
if k not in ('password','signature','action_id', 'context_lang', 'context_tz','company_id'):
|
||||
ok=False
|
||||
if ok:
|
||||
@tools.cache()
|
||||
def company_get(self, cr, uid, uid2, context=None):
|
||||
return self._get_company(cr, uid, context=context, uid2=uid2)
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if not hasattr(ids, '__iter__'):
|
||||
ids = [ids]
|
||||
if ids == [uid]:
|
||||
for key in values.keys():
|
||||
if not (key in ('view', 'password','signature','action_id', 'company_id') or key.startswith('context_')):
|
||||
break
|
||||
else:
|
||||
uid = 1
|
||||
res = super(users, self).write(cr, uid, ids, values, *args, **argv)
|
||||
res = super(users, self).write(cr, uid, ids, values, context=context)
|
||||
|
||||
# clear caches linked to the users
|
||||
self.company_get.clear_cache(cr.dbname)
|
||||
# Restart the cache on the company_get method
|
||||
self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
|
||||
clear = partial(self.pool.get('ir.rule').clear_cache, cr)
|
||||
map(clear, ids)
|
||||
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
Users Groups
|
||||
-->
|
||||
<record model="res.groups" id="group_system">
|
||||
<field name="name">Administrator / Configuration</field>
|
||||
<field name="name">Administration / Configuration</field>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" id="group_erp_manager">
|
||||
<field name="name">Administrator / Access Rights</field>
|
||||
<field name="name">Administration / Access Rights</field>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" id="group_user">
|
||||
@@ -18,7 +18,7 @@
|
||||
</record>
|
||||
|
||||
<record model="res.groups" context="{'noadmin':True}" id="group_multi_company">
|
||||
<field name="name">Multi Companies</field>
|
||||
<field name="name">Useability / Multi Companies</field>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" context="{'noadmin':True}" id="group_extended">
|
||||
@@ -51,7 +51,6 @@
|
||||
</record>
|
||||
|
||||
<record id="base.menu_translation" model="ir.ui.menu">
|
||||
<!-- FIXME: a "Usability / Translations" group ?? -->
|
||||
<field eval="[(6,0,[ref('base.group_system')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"access_ir_attachment_group_system","ir_attachment group_system","model_ir_attachment",,1,1,1,1
|
||||
"access_ir_attachment_all","ir_attachment all","model_ir_attachment",,1,0,0,0
|
||||
"access_ir_attachment_group_user","ir_attachment group_user","model_ir_attachment",group_user,1,1,1,1
|
||||
"access_ir_cron_group_cron","ir_cron group_cron","model_ir_cron","group_system",1,1,1,1
|
||||
"access_ir_cron_user","ir_cron user","model_ir_cron","group_user",1,0,0,0
|
||||
"access_ir_default_group_system","ir_default group_system","model_ir_default",,1,1,1,1
|
||||
"access_ir_exports_group_system","ir_exports group_system","model_ir_exports",,1,1,1,1
|
||||
"access_ir_exports_line_group_system","ir_exports_line group_system","model_ir_exports_line",,1,1,1,1
|
||||
"access_ir_exports_group_system","ir_exports group_system","model_ir_exports",base.group_user,1,1,1,1
|
||||
"access_ir_exports_line_group_system","ir_exports_line group_system","model_ir_exports_line",base.group_user,1,1,1,1
|
||||
"access_ir_model_group_erp_manager","ir_model group_erp_manager","model_ir_model","group_erp_manager",1,1,1,1
|
||||
"access_ir_model_access_group_erp_manager","ir_model_access_group_erp_manager","model_ir_model_access","group_erp_manager",1,1,1,1
|
||||
"access_ir_model_grid_group_erp_manager","ir_model_access_grid_group_erp_manager","model_ir_model_grid","group_erp_manager",1,1,1,1
|
||||
@@ -24,10 +25,11 @@
|
||||
"access_ir_report_custom_fields_group_system","ir_report_custom_fields group_system","model_ir_report_custom_fields",,1,0,0,0
|
||||
"access_ir_rule_group_user","ir_rule group_user","model_ir_rule",,1,0,0,0
|
||||
"access_ir_rule_group_erp_manager","ir_rule group_erp_manager","model_ir_rule","group_erp_manager",1,1,1,1
|
||||
"access_ir_sequence_group_user","ir_sequence group_user","model_ir_sequence",,1,1,1,1
|
||||
"access_ir_sequence_group_user","ir_sequence group_user","model_ir_sequence",,1,0,0,0
|
||||
"access_ir_sequence_type_group_user","ir_sequence_type group_user","model_ir_sequence_type",,1,0,0,0
|
||||
"access_ir_translation_group_system","ir_translation group_system","model_ir_translation",,1,1,1,1
|
||||
"access_ir_ui_menu_group_user","ir_ui_menu group_user","model_ir_ui_menu",,1,0,1,0
|
||||
"access_ir_translation_all","ir_translation all","model_ir_translation",,1,0,0,0
|
||||
"access_ir_translation_group_system","ir_translation group_system","model_ir_translation","group_system",1,1,1,1
|
||||
"access_ir_ui_menu_group_user","ir_ui_menu group_user","model_ir_ui_menu",,1,0,0,0
|
||||
"access_ir_ui_menu_group_system","ir_ui_menu group_system","model_ir_ui_menu","group_system",1,1,1,1
|
||||
"access_ir_ui_view_group_user","ir_ui_view group_user","model_ir_ui_view",,1,0,0,0
|
||||
"access_ir_ui_view_group_system","ir_ui_view group_system","model_ir_ui_view","group_system",1,1,1,1
|
||||
@@ -41,8 +43,8 @@
|
||||
"access_wizard_module_lang_export_group_system","wizard_module_lang_export group_system","model_wizard_module_lang_export","group_system",1,1,1,1
|
||||
"access_res_company_group_erp_manager","res_company group_erp_manager","model_res_company","group_erp_manager",1,1,1,1
|
||||
"access_res_company_group_user","res_company group_user","model_res_company",,1,0,0,0
|
||||
"access_res_config_all","res_config all","model_res_config",,1,0,0,0
|
||||
"access_res_config_users_all","res_config_users all","model_res_config_users",,1,0,0,0
|
||||
"access_res_config_all","res_config all","model_res_config",group_system,1,1,1,1
|
||||
"access_res_config_users_all","res_config_users all","model_res_config_users",group_system,1,1,1,1
|
||||
"access_res_country_group_all","res_country group_user_all","model_res_country",,1,0,0,0
|
||||
"access_res_country_state_group_all","res_country_state group_user_all","model_res_country_state",,1,0,0,0
|
||||
"access_res_country_group_user","res_country group_user","model_res_country","group_partner_manager",1,1,1,1
|
||||
@@ -69,21 +71,23 @@
|
||||
"access_res_partner_canal_group_partner_manager","res_partner_canal group_partner_manager","model_res_partner_canal","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_category_group_user","res_partner_category group_user","model_res_partner_category","group_user",1,0,0,0
|
||||
"access_res_partner_category_group_partner_manager","res_partner_category group_partner_manager","model_res_partner_category","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_event_group_user","res_partner_event group_user","model_res_partner_event","group_user",1,1,1,1
|
||||
"access_res_partner_event_group_user","res_partner_event group_user","model_res_partner_event","group_user",1,0,0,0
|
||||
"access_res_partner_event_group_partner_manager","res_partner_event group_partner_manager","model_res_partner_event","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_som_group_user","res_partner_som group_user","model_res_partner_som","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_som_group_partner_manager","res_partner_som group_partner_manager","model_res_partner_som","group_user",1,0,0,0
|
||||
"access_res_partner_title_group_user","res_partner_title group_user","model_res_partner_title","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_title_group_partner_manager","res_partner_title group_partner_manager","model_res_partner_title","group_user",1,0,0,0
|
||||
"access_res_request_group_user","res_request group_user","model_res_request",,1,1,1,1
|
||||
"access_res_request_history_group_user","res_request_history group_user","model_res_request_history",,1,1,1,1
|
||||
"access_res_partner_title_group_partner_manager","res_partner_title group_partner_manager","model_res_partner_title",,1,0,0,0
|
||||
"access_res_request_group_user","res_request group_user","model_res_request",base.group_user,1,1,1,1
|
||||
"access_res_request_history_group_user","res_request_history group_user","model_res_request_history",base.group_user,1,1,1,1
|
||||
"access_res_request_group_user_all","res_request group_user all","model_res_request",,1,0,0,0
|
||||
"access_res_request_history_group_user_all","res_request_history group_user all","model_res_request_history",,1,0,0,0
|
||||
"access_res_request_link_group_system","res_request_link group_system","model_res_request_link","group_system",1,1,1,1
|
||||
"access_res_request_link_group_user","res_request_link group_user","model_res_request_link",,1,0,0,0
|
||||
"access_res_users_all","res_users all","model_res_users",,1,0,0,0
|
||||
"access_res_users_group_erp_manager","res_users group_erp_manager","model_res_users","group_erp_manager",1,1,1,1
|
||||
"access_ir_actions_all","ir_actions_all","model_ir_actions_actions",,1,0,0,0
|
||||
"access_ir_actions_group_system","ir_actions_group_system","model_ir_actions_actions","group_system",1,1,1,1
|
||||
"access_ir_actions_act_window_all","ir_actions_act_window_all","model_ir_actions_act_window",,1,0,1,0
|
||||
"access_ir_actions_act_window_all","ir_actions_act_window_all","model_ir_actions_act_window",,1,0,0,0
|
||||
"access_ir_actions_act_window_system","ir_actions_act_window_system","model_ir_actions_act_window","group_system",1,1,1,1
|
||||
"access_ir_actions_act_window_close_all","ir_actions_act_window_close_all","model_ir_actions_act_window_close",,1,0,0,0
|
||||
"access_ir_actions_act_window_close_group_system","ir_actions_act_window_close_group_system","model_ir_actions_act_window_close","group_system",1,1,1,1
|
||||
@@ -97,7 +101,7 @@
|
||||
"access_workflow_group_system","workflow_group_system","model_workflow","group_system",1,1,1,1
|
||||
"access_workflow_activity_all","workflow_activity_all","model_workflow_activity",,1,0,0,0
|
||||
"access_workflow_activity_group_system","workflow_activity_group_system","model_workflow_activity","group_system",1,1,1,1
|
||||
"access_workflow_instance_all","workflow_instance_all","model_workflow_instance",,1,1,1,1
|
||||
"access_workflow_instance_all","workflow_instance_all","model_workflow_instance",,1,0,0,0
|
||||
"access_workflow_transition_all","workflow_transition_all","model_workflow_transition",,1,0,0,0
|
||||
"access_workflow_transition_group_system","workflow_transition_group_system","model_workflow_transition","group_system",1,1,1,1
|
||||
"access_workflow_triggers_all","workflow_triggers_all","model_workflow_triggers",,1,0,0,0
|
||||
@@ -117,13 +121,16 @@
|
||||
"access_res_bank_group_system","res_bank_group_system","model_res_bank","group_system",1,1,1,1
|
||||
"access_res_payterm_group_system","res_payterm_group_system","model_res_payterm","group_system",1,1,1,1
|
||||
"access_res_roles_group_erpmanager","res_roles_group_erp_manager","model_res_roles","group_erp_manager",1,1,1,1
|
||||
"access_res_config_view_group_erpmanager","res_config_view_erp_manager","model_res_config_view","group_erp_manager",1,1,1,1
|
||||
"access_res_config_view_group_erpmanager","res_config_view_erp_manager","model_res_config_view","group_system",1,1,1,1
|
||||
"access_res_roles_all","res_roles_all","model_res_roles",,1,0,0,0
|
||||
"access_res_config_view_all","res_config_view_all","model_res_config_view",,1,0,0,0
|
||||
"access_res_config_view_all","res_config_view_all","model_res_config_view",group_system,1,0,0,0
|
||||
"access_res_bank_group_partner_manager","res_bank_group_partner_manager","model_res_bank","group_partner_manager",1,1,1,1
|
||||
"access_res_bank_user","res_bank user","model_res_bank","group_user",1,0,0,0
|
||||
"access_maintenance_group_user","maintenance_contract group_user","model_maintenance_contract","group_maintenance_manager",1,1,1,1
|
||||
"access_maintenance_contract_module","maintenance.contract.module","model_maintenance_contract_module","group_maintenance_manager",1,1,1,1
|
||||
"access_maintenance_contract_wizard","maintenance.contract.wizard","model_maintenance_contract_wizard","group_maintenance_manager",1,1,1,1
|
||||
"access_multi_company_default user","multi_company_default User","model_multi_company_default","group_user",1,0,0,0
|
||||
"access_maintenance_group_user","maintenance_contract group_user","model_maintenance_contract","group_system",1,1,1,1
|
||||
"access_maintenance_contract_module","maintenance.contract.module","model_maintenance_contract_module","group_system",1,1,1,1
|
||||
"access_maintenance_contract_wizard","maintenance.contract.wizard","model_maintenance_contract_wizard","group_system",1,1,1,1
|
||||
"access_multi_company_default user","multi_company_default all","model_multi_company_default",,1,0,0,0
|
||||
"access_multi_company_default manager","multi_company_default Manager","model_multi_company_default","group_erp_manager",1,1,1,1
|
||||
"access_ir_filter all","ir_filters all","model_ir_filters",,1,0,0,0
|
||||
"access_ir_filter employee","ir_filters employee","model_ir_filters","group_user",1,1,1,1
|
||||
"access_res_log all","res_log all","model_res_log",,1,0,1,1
|
||||
|
||||
|
@@ -0,0 +1,57 @@
|
||||
- |
|
||||
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.
|
||||
-
|
||||
1. Try a few common expressions to verify they work with safe_eval
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import safe_eval
|
||||
expected = (1, {"a": 9 * 2}, (True, False, None))
|
||||
actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
|
||||
assert actual == expected, "Simple python expressions are not working with safe_eval"
|
||||
-
|
||||
2. Try simple literal definition to verify it works with literal_eval
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
expected = (1, {"a": 9}, (True, False, None))
|
||||
actual = literal_eval('(1, {"a": 9}, (True, False, None))')
|
||||
assert actual == expected, "Simple python expressions are not working with literal_eval"
|
||||
-
|
||||
3. Try arithmetic expression in literal_eval to verify it does not work
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
try:
|
||||
literal_eval('(1, {"a": 2*9}, (True, False, None))')
|
||||
assert False, "literal_eval should not accept arithmetic expressions"
|
||||
except ValueError:
|
||||
pass
|
||||
-
|
||||
4. Try forbidden expressions in literal_eval to verify they are not allowed
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
try:
|
||||
literal_eval('{"a": True.__class__}')
|
||||
assert False, "literal_eval should accept only literals"
|
||||
except ValueError:
|
||||
pass
|
||||
-
|
||||
5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import safe_eval
|
||||
try:
|
||||
safe_eval('open("/etc/passwd","r")')
|
||||
assert False, "safe_eval should not allow arbitrary expressions"
|
||||
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"
|
||||
|
||||
@@ -49,7 +49,7 @@ def to_decode(s):
|
||||
return s
|
||||
|
||||
def start_server(root_path, port, netport, addons_path):
|
||||
os.system('python2.5 %sopenerp-server.py --pidfile=openerp.pid --port=%s --net_port=%s --addons-path=%s' %(root_path, str(port),str(netport),addons_path))
|
||||
os.system('python2.5 %sopenerp-server.py --pidfile=openerp.pid --no-xmlrpcs --xmlrpc-port=%s --netrpc-port=%s --addons-path=%s' %(root_path, str(port),str(netport),addons_path))
|
||||
def clean():
|
||||
if os.path.isfile('openerp.pid'):
|
||||
ps = open('openerp.pid')
|
||||
|
||||
@@ -201,6 +201,9 @@
|
||||
<rng:optional> <rng:attribute name="target"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="key2"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="groups"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="limit"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="usage"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="auto_refresh"/> </rng:optional>
|
||||
<rng:empty />
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
+22
-24
@@ -31,6 +31,7 @@
|
||||
# required
|
||||
# size
|
||||
#
|
||||
from collections import defaultdict
|
||||
import string
|
||||
import netsvc
|
||||
import sys
|
||||
@@ -245,7 +246,7 @@ class selection(_column):
|
||||
|
||||
#
|
||||
# Values: (0, 0, { fields }) create
|
||||
# (1, ID, { fields }) modification
|
||||
# (1, ID, { fields }) update
|
||||
# (2, ID) remove (delete)
|
||||
# (3, ID) unlink one (target id or target of relation)
|
||||
# (4, ID) link
|
||||
@@ -430,10 +431,10 @@ class one2many(_column):
|
||||
context.update(self._context)
|
||||
if not values:
|
||||
values = {}
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = []
|
||||
ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
|
||||
|
||||
res = defaultdict(list)
|
||||
|
||||
ids2 = obj.pool.get(self._obj).search(cr, user, self._domain + [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
|
||||
for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
|
||||
res[r[self._fields_id]].append(r['id'])
|
||||
return res
|
||||
@@ -479,10 +480,10 @@ class one2many(_column):
|
||||
|
||||
#
|
||||
# Values: (0, 0, { fields }) create
|
||||
# (1, ID, { fields }) modification
|
||||
# (2, ID) remove
|
||||
# (3, ID) unlink
|
||||
# (4, ID) link
|
||||
# (1, ID, { fields }) update (write fields to ID)
|
||||
# (2, ID) remove (calls unlink on ID, that will also delete the relationship because of the ondelete)
|
||||
# (3, ID) unlink (delete the relationship between the two objects but does not delete ID)
|
||||
# (4, ID) link (add a relationship)
|
||||
# (5, ID) unlink all
|
||||
# (6, ?, ids) set a list of links
|
||||
#
|
||||
@@ -491,7 +492,6 @@ class many2many(_column):
|
||||
_classic_write = False
|
||||
_prefetch = False
|
||||
_type = 'many2many'
|
||||
|
||||
def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args):
|
||||
_column.__init__(self, string=string, **args)
|
||||
self._obj = obj
|
||||
@@ -779,7 +779,7 @@ class related(function):
|
||||
relation = obj._name
|
||||
res = {}.fromkeys(ids, False)
|
||||
|
||||
objlst = obj.browse(cr, uid, ids, context=context)
|
||||
objlst = obj.browse(cr, 1, ids, context=context)
|
||||
for data in objlst:
|
||||
if not data:
|
||||
continue
|
||||
@@ -806,7 +806,7 @@ class related(function):
|
||||
if self._type=='many2one':
|
||||
ids = filter(None, res.values())
|
||||
if ids:
|
||||
ng = dict(obj.pool.get(self._obj).name_get(cr, uid, ids, context=context))
|
||||
ng = dict(obj.pool.get(self._obj).name_get(cr, 1, ids, context=context))
|
||||
for r in res:
|
||||
if res[r]:
|
||||
res[r] = (res[r], ng[res[r]])
|
||||
@@ -874,12 +874,17 @@ class serialized(_column):
|
||||
class property(function):
|
||||
|
||||
def _get_default(self, obj, cr, uid, prop_name, context=None):
|
||||
from orm import browse_record
|
||||
prop = obj.pool.get('ir.property')
|
||||
domain = prop._get_domain(cr, uid, prop_name, obj._name, context)
|
||||
domain = prop._get_domain_default(cr, uid, prop_name, obj._name, context)
|
||||
ids = prop.search(cr, uid, domain, order='company_id', context=context)
|
||||
if not ids:
|
||||
return False
|
||||
return prop.get_by_id(cr, uid, ids, context=context)
|
||||
|
||||
default_value = prop.get_by_id(cr, uid, ids, context=context)
|
||||
if isinstance(default_value, browse_record):
|
||||
return default_value.id
|
||||
return default_value or False
|
||||
|
||||
def _get_by_id(self, obj, cr, uid, prop_name, ids, context=None):
|
||||
prop = obj.pool.get('ir.property')
|
||||
@@ -903,7 +908,7 @@ class property(function):
|
||||
|
||||
default_val = self._get_default(obj, cr, uid, prop_name, context)
|
||||
|
||||
if id_val and id_val != default_val:
|
||||
if id_val is not default_val:
|
||||
def_id = self._field_get(cr, uid, obj._name, prop_name)
|
||||
company = obj.pool.get('res.company')
|
||||
cid = company._company_default_get(cr, uid, obj._name, def_id,
|
||||
@@ -924,23 +929,16 @@ class property(function):
|
||||
|
||||
def _fnct_read(self, obj, cr, uid, ids, prop_name, obj_dest, context=None):
|
||||
from orm import browse_record
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
property = obj.pool.get('ir.property')
|
||||
properties = obj.pool.get('ir.property')
|
||||
|
||||
default_val = self._get_default(obj, cr, uid, prop_name, context)
|
||||
if isinstance(default_val, browse_record):
|
||||
default_val = default_val.id
|
||||
else:
|
||||
default_val = False
|
||||
|
||||
nids = self._get_by_id(obj, cr, uid, prop_name, ids, context)
|
||||
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = default_val
|
||||
for prop in property.browse(cr, uid, nids):
|
||||
for prop in properties.browse(cr, uid, nids):
|
||||
value = prop.get_by_id(context=context)
|
||||
if isinstance(value, browse_record):
|
||||
if not value.exists():
|
||||
|
||||
+79
-18
@@ -56,7 +56,7 @@ from tools.translate import _
|
||||
|
||||
import fields
|
||||
import tools
|
||||
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||
|
||||
@@ -353,8 +353,8 @@ def get_pg_type(f):
|
||||
f_type = ('int4', 'INTEGER')
|
||||
else:
|
||||
f_type = ('varchar', 'VARCHAR(%d)' % f_size)
|
||||
elif isinstance(f, fields.function) and eval('fields.'+(f._type)) in type_dict:
|
||||
t = eval('fields.'+(f._type))
|
||||
elif isinstance(f, fields.function) and eval('fields.'+(f._type),globals()) in type_dict:
|
||||
t = eval('fields.'+(f._type), globals())
|
||||
f_type = (type_dict[t], type_dict[t])
|
||||
elif isinstance(f, fields.function) and f._type == 'float':
|
||||
if f.digits:
|
||||
@@ -987,7 +987,7 @@ class orm_template(object):
|
||||
return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' )
|
||||
#Raising Uncaught exception
|
||||
return (-1, res, 'Line ' + str(counter) +' : ' + str(e), '' )
|
||||
|
||||
|
||||
for lang in translate:
|
||||
context2 = context.copy()
|
||||
context2['lang'] = lang
|
||||
@@ -1224,21 +1224,26 @@ class orm_template(object):
|
||||
attrs = {'views': views}
|
||||
if node.get('widget') and node.get('widget') == 'selection':
|
||||
if not check_group(node):
|
||||
# the field is just invisible. default value must be in the selection
|
||||
name = node.get('name')
|
||||
default = self.default_get(cr, user, [name], context=context).get(name)
|
||||
if default:
|
||||
attrs['selection'] = relation.name_get(cr, 1, default, context=context)
|
||||
attrs['selection'] = relation.name_get(cr, 1, [default], context=context)
|
||||
else:
|
||||
attrs['selection'] = []
|
||||
# We can not use the 'string' domain has it is defined according to the record !
|
||||
else:
|
||||
# If domain and context are strings, we keep them for client-side, otherwise
|
||||
# we evaluate them server-side to consider them when generating the list of
|
||||
# possible values
|
||||
# TODO: find a way to remove this hack, by allow dynamic domains
|
||||
dom = []
|
||||
if column._domain and not isinstance(column._domain, (str, unicode)):
|
||||
if column._domain and not isinstance(column._domain, basestring):
|
||||
dom = column._domain
|
||||
dom += eval(node.get('domain','[]'), {'uid':user, 'time':time})
|
||||
context.update(eval(node.get('context','{}')))
|
||||
attrs['selection'] = relation._name_search(cr, user, '', dom, context=context, limit=None, name_get_uid=1)
|
||||
search_context = dict(context)
|
||||
if column._context and not isinstance(column._context, basestring):
|
||||
search_context.update(column._context)
|
||||
attrs['selection'] = relation._name_search(cr, 1, '', dom, context=search_context, limit=None, name_get_uid=1)
|
||||
if (node.get('required') and not int(node.get('required'))) or not column.required:
|
||||
attrs['selection'].append((False,''))
|
||||
fields[node.get('name')] = attrs
|
||||
@@ -1951,9 +1956,9 @@ class orm_memory(orm_template):
|
||||
f = True
|
||||
for arg in result:
|
||||
if arg[1] =='=':
|
||||
val =eval('data[arg[0]]'+'==' +' arg[2]')
|
||||
val =eval('data[arg[0]]'+'==' +' arg[2]', locals())
|
||||
elif arg[1] in ['<','>','in','not in','<=','>=','<>']:
|
||||
val =eval('data[arg[0]]'+arg[1] +' arg[2]')
|
||||
val =eval('data[arg[0]]'+arg[1] +' arg[2]', locals())
|
||||
elif arg[1] in ['ilike']:
|
||||
if str(data[arg[0]]).find(str(arg[2]))!=-1:
|
||||
val= True
|
||||
@@ -2103,8 +2108,7 @@ class orm(orm_template):
|
||||
days = calendar.monthrange(dt.year, dt.month)[1]
|
||||
|
||||
d[groupby] = datetime.datetime.strptime(d[groupby][:10],'%Y-%m-%d').strftime('%B %Y')
|
||||
if not context.get('group_by_no_leaf', False):
|
||||
d['__domain'] = [(groupby,'>=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-01','%Y-%m-%d').strftime('%Y-%m-%d') or False),\
|
||||
d['__domain'] = [(groupby,'>=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-01','%Y-%m-%d').strftime('%Y-%m-%d') or False),\
|
||||
(groupby,'<=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-' + str(days),'%Y-%m-%d').strftime('%Y-%m-%d') or False)] + domain
|
||||
elif fget[groupby]['type'] == 'many2one':
|
||||
d[groupby] = d[groupby] and ((type(d[groupby])==type(1)) and d[groupby] or d[groupby][1]) or ''
|
||||
@@ -2612,6 +2616,7 @@ class orm(orm_template):
|
||||
fld_def = ((f in self._columns) and self._columns[f]) \
|
||||
or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \
|
||||
or False
|
||||
|
||||
if isinstance(fld_def, fields.property):
|
||||
property_obj = self.pool.get('ir.property')
|
||||
prop_value = property_obj.get(cr, uid, f, self._name, context=context)
|
||||
@@ -2621,7 +2626,8 @@ class orm(orm_template):
|
||||
else:
|
||||
value[f] = prop_value
|
||||
else:
|
||||
value[f] = False
|
||||
if f not in value:
|
||||
value[f] = False
|
||||
|
||||
# get the default values set by the user and override the default
|
||||
# values defined in the object
|
||||
@@ -2688,6 +2694,32 @@ class orm(orm_template):
|
||||
self._inherit_fields = res
|
||||
self._inherits_reload_src()
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Proxies attribute accesses to the `inherits` parent so we can call methods defined on the inherited parent
|
||||
(though inherits doesn't use Python inheritance).
|
||||
Handles translating between local ids and remote ids.
|
||||
Known issue: doesn't work correctly when using python's own super(), don't involve inherit-based inheritance
|
||||
when you have inherits.
|
||||
"""
|
||||
for model, field in self._inherits.iteritems():
|
||||
proxy = self.pool.get(model)
|
||||
if hasattr(proxy, name):
|
||||
attribute = getattr(proxy, name)
|
||||
if not hasattr(attribute, '__call__'):
|
||||
return attribute
|
||||
break
|
||||
else:
|
||||
return super(orm, self).__getattr__(name)
|
||||
|
||||
def _proxy(cr, uid, ids, *args, **kwargs):
|
||||
objects = self.browse(cr, uid, ids, kwargs.get('context', None))
|
||||
lst = [obj[field].id for obj in objects if obj[field]]
|
||||
return getattr(proxy, name)(cr, uid, lst, *args, **kwargs)
|
||||
|
||||
return _proxy
|
||||
|
||||
|
||||
def fields_get(self, cr, user, fields=None, context=None):
|
||||
"""
|
||||
Get the description of list of fields
|
||||
@@ -3068,11 +3100,27 @@ class orm(orm_template):
|
||||
|
||||
vals format for relational field type.
|
||||
|
||||
+ many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])])
|
||||
+ many2many field :
|
||||
|
||||
For write operation on a many2many fields a list of tuple is
|
||||
expected. The folowing tuples are accepted:
|
||||
(0, 0, { fields }) create
|
||||
(1, ID, { fields }) update (write fields to ID)
|
||||
(2, ID) remove (calls unlink on ID, that will also delete the relationship because of the ondelete)
|
||||
(3, ID) unlink (delete the relationship between the two objects but does not delete ID)
|
||||
(4, ID) link (add a relationship)
|
||||
(5, ID) unlink all
|
||||
(6, 0, list of ids) set a list of links
|
||||
|
||||
Example:
|
||||
|
||||
[(6, 0, [8, 5, 6, 4])] set the many2many to ids [8, 5, 6, 4]
|
||||
|
||||
+ one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})])
|
||||
+ many2one field : ID of related record
|
||||
+ reference field : model name, id (example: 'product.product, 5')
|
||||
|
||||
|
||||
"""
|
||||
readonly = None
|
||||
for field in vals.copy():
|
||||
@@ -3112,9 +3160,10 @@ class orm(orm_template):
|
||||
ids = [ids]
|
||||
|
||||
self._check_concurrency(cr, ids, context)
|
||||
|
||||
self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context)
|
||||
|
||||
result = self._store_get_values(cr, user, ids, vals.keys(), context) or []
|
||||
|
||||
# No direct update of parent_left/right
|
||||
vals.pop('parent_left', None)
|
||||
vals.pop('parent_right', None)
|
||||
@@ -3202,7 +3251,6 @@ class orm(orm_template):
|
||||
if c[0].startswith('default_'):
|
||||
del rel_context[c[0]]
|
||||
|
||||
result = []
|
||||
for field in upd_todo:
|
||||
for id in ids:
|
||||
result += self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context) or []
|
||||
@@ -3273,8 +3321,19 @@ class orm(orm_template):
|
||||
cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance,pleft-position+distance, pleft+distance, pright+distance))
|
||||
|
||||
result += self._store_get_values(cr, user, ids, vals.keys(), context)
|
||||
result.sort()
|
||||
|
||||
done = {}
|
||||
for order, object, ids, fields in result:
|
||||
self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
|
||||
key = (object,tuple(fields))
|
||||
done.setdefault(key, {})
|
||||
# avoid to do several times the same computation
|
||||
todo = []
|
||||
for id in ids:
|
||||
if id not in done[key]:
|
||||
done[key][id] = True
|
||||
todo.append(id)
|
||||
self.pool.get(object)._store_set_values(cr, user, todo, fields, context)
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
for id in ids:
|
||||
@@ -3537,6 +3596,8 @@ class orm(orm_template):
|
||||
return result2
|
||||
|
||||
def _store_set_values(self, cr, uid, ids, fields, context):
|
||||
if not ids:
|
||||
return True
|
||||
field_flag = False
|
||||
field_dict = {}
|
||||
if self._log_access:
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ class osv_pool(netsvc.Service):
|
||||
raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
|
||||
res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
|
||||
if res is None:
|
||||
self.logger.warning('RPC methods cannot return `None` at the moment!')
|
||||
self.logger.warning('The method %s of the object %s can not return `None` !', method, obj)
|
||||
cr.commit()
|
||||
except Exception:
|
||||
cr.rollback()
|
||||
|
||||
@@ -24,6 +24,7 @@ import time
|
||||
import netsvc
|
||||
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import print_xml
|
||||
import render
|
||||
from interface import report_int
|
||||
|
||||
@@ -23,6 +23,7 @@ import os,types
|
||||
from lxml import etree
|
||||
import netsvc
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval
|
||||
import print_fnc
|
||||
import copy
|
||||
from osv.orm import browse_null, browse_record
|
||||
@@ -111,7 +112,7 @@ class document(object):
|
||||
#Pinky: Why not this ? eval(expr, browser) ?
|
||||
# name = browser.name
|
||||
# data_dict = browser._data[self.get_value(browser, 'id')]
|
||||
return eval(expr, {}, {'obj': record})
|
||||
return safe_eval(expr, {}, {'obj': record})
|
||||
|
||||
def parse_node(self, node, parent, browser, datas=None):
|
||||
attrs = self.node_attrs_get(node)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from report.interface import report_int
|
||||
import pooler
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from lxml import etree
|
||||
from report import render
|
||||
import locale
|
||||
@@ -128,8 +129,7 @@ class report_printscreen_list(report_int):
|
||||
strmax = (pageSize[0]-40) * 2.8346
|
||||
temp = []
|
||||
tsum = []
|
||||
count = len(fields_order)
|
||||
for i in range(0, count):
|
||||
for i in range(0, len(fields_order)):
|
||||
temp.append(0)
|
||||
tsum.append(0)
|
||||
ince = -1;
|
||||
@@ -218,7 +218,7 @@ class report_printscreen_list(report_int):
|
||||
else:
|
||||
col.text = '/'
|
||||
node_line = etree.SubElement(lines, 'row')
|
||||
for f in range(0, count+1):
|
||||
for f in range(0, len(fields_order)):
|
||||
col = etree.SubElement(node_line, 'col', para='group', tree='no')
|
||||
if tsum[f] != None:
|
||||
if tsum[f] >= 0.01 :
|
||||
|
||||
@@ -34,6 +34,7 @@ import os
|
||||
from lxml import etree
|
||||
import base64
|
||||
from reportlab.platypus.doctemplate import ActionFlowable
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
encoding = 'utf-8'
|
||||
|
||||
@@ -574,7 +575,7 @@ class _rml_flowable(object):
|
||||
def _illustration(self, node):
|
||||
class Illustration(platypus.flowables.Flowable):
|
||||
def __init__(self, node, localcontext, styles, self2):
|
||||
self.localcontext = localcontext.copy()
|
||||
self.localcontext = (localcontext or {}).copy()
|
||||
self.node = node
|
||||
self.styles = styles
|
||||
self.width = utils.unit_get(node.get('width'))
|
||||
|
||||
@@ -42,6 +42,7 @@ from lxml import etree
|
||||
import copy
|
||||
import locale
|
||||
import traceback, sys
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
_regex = re.compile('\[\[(.+?)\]\]')
|
||||
|
||||
@@ -54,7 +55,7 @@ def xml2str(s):
|
||||
def _child_get(node, self=None, tagname=None):
|
||||
for n in node:
|
||||
if self and self.localcontext and n.get('rml_loop'):
|
||||
oldctx = self.localcontext
|
||||
oldctx = dict(self.localcontext)
|
||||
|
||||
for ctx in eval(n.get('rml_loop'),{}, self.localcontext):
|
||||
self.localcontext.update(ctx)
|
||||
|
||||
@@ -23,6 +23,7 @@ import re
|
||||
import reportlab
|
||||
import reportlab.lib.units
|
||||
from lxml import etree
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
_regex = re.compile('\[\[(.+?)\]\]')
|
||||
|
||||
@@ -128,4 +129,4 @@ def attr_get(node, attrs, dict={}):
|
||||
res[key] = unit_get(node.get(key))
|
||||
return res
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
+24
-17
@@ -120,10 +120,6 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server):
|
||||
self.server = ThreadedHTTPServer((interface, port), handler)
|
||||
self.server.vdirs = []
|
||||
self.server.logRequests = True
|
||||
netsvc.Logger().notifyChannel(
|
||||
"web-services", netsvc.LOG_INFO,
|
||||
"starting HTTPS service at %s port %d" %
|
||||
(interface or '0.0.0.0', port,))
|
||||
except Exception, e:
|
||||
netsvc.Logger().notifyChannel(
|
||||
'httpd', netsvc.LOG_CRITICAL,
|
||||
@@ -155,6 +151,10 @@ class HttpDaemon(BaseHttpDaemon):
|
||||
def __init__(self, interface, port):
|
||||
super(HttpDaemon, self).__init__(interface, port,
|
||||
handler=MultiHandler2)
|
||||
netsvc.Logger().notifyChannel(
|
||||
"web-services", netsvc.LOG_INFO,
|
||||
"starting HTTP service at %s port %d" %
|
||||
(interface or '0.0.0.0', port,))
|
||||
|
||||
class HttpSDaemon(BaseHttpDaemon):
|
||||
def __init__(self, interface, port):
|
||||
@@ -166,19 +166,23 @@ class HttpSDaemon(BaseHttpDaemon):
|
||||
'httpd-ssl', netsvc.LOG_CRITICAL,
|
||||
"Can not load the certificate and/or the private key files")
|
||||
raise
|
||||
netsvc.Logger().notifyChannel(
|
||||
"web-services", netsvc.LOG_INFO,
|
||||
"starting HTTPS service at %s port %d" %
|
||||
(interface or '0.0.0.0', port,))
|
||||
|
||||
httpd = None
|
||||
httpsd = None
|
||||
|
||||
def init_servers():
|
||||
global httpd, httpsd
|
||||
if tools.config.get_misc('httpd','enable', True):
|
||||
httpd = HttpDaemon(tools.config.get_misc('httpd','interface', ''), \
|
||||
int(tools.config.get_misc('httpd','port', tools.config.get('port',8069))))
|
||||
if tools.config.get('xmlrpc'):
|
||||
httpd = HttpDaemon(tools.config.get('xmlrpc_interface', ''),
|
||||
int(tools.config.get('xmlrpc_port', 8069)))
|
||||
|
||||
if tools.config.get_misc('httpsd','enable', False):
|
||||
httpsd = HttpSDaemon(tools.config.get_misc('httpsd','interface', ''), \
|
||||
int(tools.config.get_misc('httpsd','port', 8071)))
|
||||
if tools.config.get('xmlrpcs'):
|
||||
httpsd = HttpSDaemon(tools.config.get('xmlrpcs_interface', ''),
|
||||
int(tools.config.get('xmlrpcs_port', 8071)))
|
||||
|
||||
def reg_http_service(hts, secure_only = False):
|
||||
""" Register some handler to httpd.
|
||||
@@ -226,14 +230,17 @@ class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,SimpleXMLRPCSer
|
||||
|
||||
|
||||
def init_xmlrpc():
|
||||
if not tools.config.get_misc('xmlrpc','enable', True):
|
||||
return
|
||||
reg_http_service(HTTPDir('/xmlrpc/',XMLRPCRequestHandler))
|
||||
# Example of http file serving:
|
||||
# reg_http_service(HTTPDir('/test/',HTTPHandler))
|
||||
netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO,
|
||||
"Registered XML-RPC over HTTP")
|
||||
if tools.config.get('xmlrpc', False):
|
||||
# Example of http file serving:
|
||||
# reg_http_service(HTTPDir('/test/',HTTPHandler))
|
||||
reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler))
|
||||
netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO,
|
||||
"Registered XML-RPC over HTTP")
|
||||
|
||||
if tools.config.get('xmlrpcs', False):
|
||||
reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, True))
|
||||
netsvc.Logger().notifyChannel('web-services', netsvc.LOG_INFO,
|
||||
"Registered XML-RPC over HTTPS")
|
||||
|
||||
class OerpAuthProxy(AuthProxy):
|
||||
""" Require basic authentication..
|
||||
|
||||
@@ -23,14 +23,16 @@
|
||||
|
||||
|
||||
"""
|
||||
import netsvc
|
||||
import threading
|
||||
import tools
|
||||
import os
|
||||
import logging
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import netsvc
|
||||
import tiny_socket
|
||||
import tools
|
||||
|
||||
class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher):
|
||||
def __init__(self, sock, threads):
|
||||
@@ -58,27 +60,34 @@ class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher):
|
||||
self.threads.remove(self)
|
||||
self.running = False
|
||||
return False
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
msg = ts.myreceive()
|
||||
except:
|
||||
self.threads.remove(self)
|
||||
self.running = False
|
||||
return False
|
||||
try:
|
||||
result = self.dispatch(msg[0], msg[1], msg[2:])
|
||||
ts.mysend(result)
|
||||
except socket.timeout:
|
||||
#terminate this channel because other endpoint is gone
|
||||
break
|
||||
except netsvc.OpenERPDispatcherException, e:
|
||||
try:
|
||||
new_e = Exception(tools.exception_to_unicode(e.exception)) # avoid problems of pickeling
|
||||
logging.getLogger('web-services').debug("netrpc: rpc-dispatching exception", exc_info=True)
|
||||
ts.mysend(new_e, exception=True, traceback=e.traceback)
|
||||
except:
|
||||
self.running = False
|
||||
except Exception:
|
||||
#terminate this channel if we can't properly send back the error
|
||||
logging.getLogger('web-services').exception("netrpc: cannot deliver exception message to client")
|
||||
break
|
||||
except Exception, e:
|
||||
# this code should not be reachable, therefore we warn
|
||||
netsvc.Logger().notifyChannel("net-rpc", netsvc.LOG_WARNING, "exception: %s" % str(e))
|
||||
break
|
||||
try:
|
||||
tb = getattr(e, 'traceback', sys.exc_info())
|
||||
tb_s = "".join(traceback.format_exception(*tb))
|
||||
logging.getLogger('web-services').debug("netrpc: communication-level exception", exc_info=True)
|
||||
ts.mysend(e, exception=True, traceback=tb_s)
|
||||
except Exception, ex:
|
||||
#terminate this channel if we can't properly send back the error
|
||||
logging.getLogger('web-services').exception("netrpc: cannot deliver exception message to client")
|
||||
break
|
||||
|
||||
self.threads.remove(self)
|
||||
self.running = False
|
||||
@@ -123,7 +132,6 @@ class TinySocketServerThread(threading.Thread,netsvc.Server):
|
||||
"Netrpc: %d threads" % len(self.threads))
|
||||
self.socket.close()
|
||||
except Exception, e:
|
||||
import logging
|
||||
logging.getLogger('web-services').warning("Netrpc: closing because of exception %s" % str(e))
|
||||
self.socket.close()
|
||||
return False
|
||||
@@ -152,6 +160,7 @@ netrpcd = None
|
||||
|
||||
def init_servers():
|
||||
global netrpcd
|
||||
if tools.config.get_misc('netrpcd','enable', True):
|
||||
netrpcd = TinySocketServerThread(tools.config.get_misc('netrpcd','interface', ''), \
|
||||
int(tools.config.get_misc('netrpcd','port', tools.config.get('netport', 8070))))
|
||||
if tools.config.get('netrpc', False):
|
||||
netrpcd = TinySocketServerThread(
|
||||
tools.config.get('netrpc_interface', ''),
|
||||
int(tools.config.get('netrpc_port', 8070)))
|
||||
|
||||
@@ -22,12 +22,11 @@
|
||||
import pooler
|
||||
import tools
|
||||
|
||||
# When rejecting a password, we need to give as little info as possible
|
||||
# When rejecting a password, hide the traceback
|
||||
class ExceptionNoTb(Exception):
|
||||
def __init__(self, msg ):
|
||||
# self.message = msg # No need in Python 2.6
|
||||
def __init__(self, msg):
|
||||
super(ExceptionNoTb, self).__init__(msg)
|
||||
self.traceback = ('','','')
|
||||
self.args = (msg, '')
|
||||
|
||||
def login(db, login, password):
|
||||
pool = pooler.get_pool(db)
|
||||
@@ -38,7 +37,7 @@ def check_super(passwd):
|
||||
if passwd == tools.config['admin_passwd']:
|
||||
return True
|
||||
else:
|
||||
raise ExceptionNoTb('AccessDenied')
|
||||
raise ExceptionNoTb('AccessDenied: Invalid super administrator password.')
|
||||
|
||||
def check(db, uid, passwd):
|
||||
pool = pooler.get_pool(db)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- 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#
|
||||
#
|
||||
# WARNING: This program as such is intended to be used by professional
|
||||
# programmers who take the whole responsability of assessing all potential
|
||||
|
||||
+38
-33
@@ -40,10 +40,12 @@ class configmanager(object):
|
||||
def __init__(self, fname=None):
|
||||
self.options = {
|
||||
'email_from':False,
|
||||
'interface': '', # this will bind the server to all interfaces
|
||||
'port': 8069,
|
||||
'netinterface': '',
|
||||
'netport': 8070,
|
||||
'xmlrpc_interface': '', # this will bind the server to all interfaces
|
||||
'xmlrpc_port': 8069,
|
||||
'netrpc_interface': '',
|
||||
'netrpc_port': 8070,
|
||||
'xmlrpcs_interface': '', # this will bind the server to all interfaces
|
||||
'xmlrpcs_port': 8071,
|
||||
'db_host': False,
|
||||
'db_port': False,
|
||||
'db_name': False,
|
||||
@@ -53,7 +55,7 @@ class configmanager(object):
|
||||
'reportgz': False,
|
||||
'netrpc': True,
|
||||
'xmlrpc': True,
|
||||
'soap': False,
|
||||
'xmlrpcs': True,
|
||||
'translate_in': None,
|
||||
'translate_out': None,
|
||||
'language': None,
|
||||
@@ -74,7 +76,6 @@ class configmanager(object):
|
||||
'smtp_password': False,
|
||||
'stop_after_init': False, # this will stop the server after initialization
|
||||
'price_accuracy': 2,
|
||||
'secure' : False,
|
||||
'syslog' : False,
|
||||
'log_level': logging.INFO,
|
||||
'assert_exit_level': logging.ERROR, # level above which a failed assert will be raised
|
||||
@@ -101,12 +102,30 @@ class configmanager(object):
|
||||
help="save configuration to ~/.openerp_serverrc")
|
||||
parser.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
|
||||
|
||||
parser.add_option("-n", "--interface", dest="interface", help="specify the TCP IP address")
|
||||
parser.add_option("-p", "--port", dest="port", help="specify the TCP port", type="int")
|
||||
parser.add_option("--net_interface", dest="netinterface", help="specify the TCP IP address for netrpc")
|
||||
parser.add_option("--net_port", dest="netport", help="specify the TCP port for netrpc", type="int")
|
||||
parser.add_option("--no-netrpc", dest="netrpc", action="store_false", help="disable netrpc")
|
||||
parser.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", help="disable xmlrpc")
|
||||
group = optparse.OptionGroup(parser, "XML-RPC Configuration")
|
||||
group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", help="specify the TCP IP address for the XML-RPC protocol")
|
||||
group.add_option("--xmlrpc-port", dest="xmlrpc_port", help="specify the TCP port for the XML-RPC protocol", type="int")
|
||||
group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", help="disable the XML-RPC protocol")
|
||||
parser.add_option_group(group)
|
||||
|
||||
title = "XML-RPC Secure Configuration"
|
||||
if not self.has_ssl:
|
||||
title += " (disabled as ssl is unavailable)"
|
||||
|
||||
group = optparse.OptionGroup(parser, title)
|
||||
group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", help="specify the TCP IP address for the XML-RPC Secure protocol")
|
||||
group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", help="specify the TCP port for the XML-RPC Secure protocol", type="int")
|
||||
group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", help="disable the XML-RPC Secure protocol")
|
||||
group.add_option("--cert-file", dest="secure_cert_file", default="server.cert", help="specify the certificate file for the SSL connection")
|
||||
group.add_option("--pkey-file", dest="secure_pkey_file", default="server.pkey", help="specify the private key file for the SSL connection")
|
||||
parser.add_option_group(group)
|
||||
|
||||
group = optparse.OptionGroup(parser, "NET-RPC Configuration")
|
||||
group.add_option("--netrpc-interface", dest="netrpc_interface", help="specify the TCP IP address for the NETRPC protocol")
|
||||
group.add_option("--netrpc-port", dest="netrpc_port", help="specify the TCP port for the NETRPC protocol", type="int")
|
||||
group.add_option("--no-netrpc", dest="netrpc", action="store_false", help="disable the NETRPC protocol")
|
||||
parser.add_option_group(group)
|
||||
|
||||
parser.add_option("-i", "--init", dest="init", help="init a module (use \"all\" for all modules)")
|
||||
parser.add_option("--without-demo", dest="without_demo",
|
||||
help="load demo data for a module (use \"all\" for all modules)", default=False)
|
||||
@@ -124,18 +143,6 @@ class configmanager(object):
|
||||
help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
|
||||
parser.add_option('--price_accuracy', dest='price_accuracy', default='2', help='deprecated since v6.0, replaced by module decimal_precision')
|
||||
|
||||
if self.has_ssl:
|
||||
group = optparse.OptionGroup(parser, "SSL Configuration")
|
||||
group.add_option("-S", "--secure", dest="secure",
|
||||
help="launch server over https instead of http")
|
||||
group.add_option("--cert-file", dest="secure_cert_file",
|
||||
default="server.cert",
|
||||
help="specify the certificate file for the SSL connection")
|
||||
group.add_option("--pkey-file", dest="secure_pkey_file",
|
||||
default="server.pkey",
|
||||
help="specify the private key file for the SSL connection")
|
||||
parser.add_option_group(group)
|
||||
|
||||
# Testing Group
|
||||
group = optparse.OptionGroup(parser, "Testing Configuration")
|
||||
group.add_option("--test-disable", action="store_true", dest="test_disable",
|
||||
@@ -248,15 +255,14 @@ class configmanager(object):
|
||||
if self.options['pidfile'] in ('None', 'False'):
|
||||
self.options['pidfile'] = False
|
||||
|
||||
keys = ['interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host',
|
||||
keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
|
||||
'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout','smtp_ssl',
|
||||
'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy',
|
||||
'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path',
|
||||
'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',]
|
||||
|
||||
if self.has_ssl:
|
||||
keys.extend(['secure_cert_file', 'secure_pkey_file'])
|
||||
keys.append('secure')
|
||||
'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
|
||||
'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
|
||||
'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
|
||||
'secure_cert_file', 'secure_pkey_file'
|
||||
]
|
||||
|
||||
for arg in keys:
|
||||
if getattr(opt, arg):
|
||||
@@ -266,8 +272,7 @@ class configmanager(object):
|
||||
'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
|
||||
'list_db', 'server_actions_allow_code']
|
||||
|
||||
if self.has_ssl and not self.options['secure']:
|
||||
keys.append('secure')
|
||||
keys.append('xmlrpcs')
|
||||
|
||||
for arg in keys:
|
||||
if getattr(opt, arg) is not None:
|
||||
|
||||
+25
-18
@@ -37,6 +37,12 @@ import pooler
|
||||
from config import config
|
||||
from yaml_import import convert_yaml_import
|
||||
|
||||
# Import of XML records requires the unsafe eval as well,
|
||||
# almost everywhere, which is ok because it supposedly comes
|
||||
# from trusted data, but at least we make it obvious now.
|
||||
unsafe_eval = eval
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class ConvertError(Exception):
|
||||
def __init__(self, doc, orig_excpt):
|
||||
self.d = doc
|
||||
@@ -62,7 +68,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
f_search = node.get("search",'').encode('utf-8')
|
||||
f_use = node.get("use",'id').encode('ascii')
|
||||
f_name = node.get("name",'').encode('utf-8')
|
||||
q = eval(f_search, idref)
|
||||
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]))
|
||||
@@ -96,10 +102,10 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
pytz=pytzclass()
|
||||
idref2['pytz'] = pytz
|
||||
try:
|
||||
return eval(a_eval, idref2)
|
||||
except:
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel("init", netsvc.LOG_WARNING, 'could eval(%s) for %s in %s, please get back and fix it!' % (a_eval,node.get('name'),context))
|
||||
return unsafe_eval(a_eval, idref2)
|
||||
except Exception, e:
|
||||
logger = logging.getLogger('init')
|
||||
logger.warning('couldn\'t eval(%s) for %s in %s, please get back and fix it!' % (a_eval,node.get('name'),context), exc_info=True)
|
||||
return ""
|
||||
if t == 'xml':
|
||||
def _process(s, idref):
|
||||
@@ -144,7 +150,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
a_eval = node.get('eval','')
|
||||
if a_eval:
|
||||
idref['ref'] = lambda x: self.id_get(cr, False, x)
|
||||
args = eval(a_eval, idref)
|
||||
args = unsafe_eval(a_eval, idref)
|
||||
for n in node:
|
||||
return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
|
||||
if return_val is not None:
|
||||
@@ -205,13 +211,13 @@ class xml_import(object):
|
||||
def get_context(self, data_node, node, eval_dict):
|
||||
data_node_context = (len(data_node) and data_node.get('context','').encode('utf8'))
|
||||
if data_node_context:
|
||||
context = eval(data_node_context, eval_dict)
|
||||
context = unsafe_eval(data_node_context, eval_dict)
|
||||
else:
|
||||
context = {}
|
||||
|
||||
node_context = node.get("context",'').encode('utf8')
|
||||
if node_context:
|
||||
context.update(eval(node_context, eval_dict))
|
||||
context.update(unsafe_eval(node_context, eval_dict))
|
||||
|
||||
return context
|
||||
|
||||
@@ -241,7 +247,7 @@ form: module.record_id""" % (xml_id,)
|
||||
d_id = rec.get("id",'')
|
||||
ids = []
|
||||
if d_search:
|
||||
ids = self.pool.get(d_model).search(cr,self.uid,eval(d_search))
|
||||
ids = self.pool.get(d_model).search(cr, self.uid, unsafe_eval(d_search))
|
||||
if d_id:
|
||||
try:
|
||||
ids.append(self.id_get(cr, d_model, d_id))
|
||||
@@ -403,7 +409,8 @@ form: module.record_id""" % (xml_id,)
|
||||
|
||||
def ref(str_id):
|
||||
return self.id_get(cr, None, str_id)
|
||||
context = eval(context)
|
||||
|
||||
context = unsafe_eval(context)
|
||||
# domain = eval(domain) # XXX need to test this line -> uid, active_id, active_ids, ...
|
||||
|
||||
res = {
|
||||
@@ -623,7 +630,7 @@ form: module.record_id""" % (xml_id,)
|
||||
if rec_id:
|
||||
ids = [self.id_get(cr, rec_model, rec_id)]
|
||||
elif rec_src:
|
||||
q = eval(rec_src, eval_dict)
|
||||
q = unsafe_eval(rec_src, eval_dict)
|
||||
ids = self.pool.get(rec_model).search(cr, uid, q, context=context)
|
||||
if rec_src_count:
|
||||
count = int(rec_src_count)
|
||||
@@ -651,14 +658,14 @@ form: module.record_id""" % (xml_id,)
|
||||
if key in brrec:
|
||||
return brrec[key]
|
||||
return dict.__getitem__(self2, key)
|
||||
globals = d()
|
||||
globals['floatEqual'] = self._assert_equals
|
||||
globals['ref'] = ref
|
||||
globals['_ref'] = ref
|
||||
globals_dict = d()
|
||||
globals_dict['floatEqual'] = self._assert_equals
|
||||
globals_dict['ref'] = ref
|
||||
globals_dict['_ref'] = ref
|
||||
for test in rec.findall('./test'):
|
||||
f_expr = test.get("expr",'').encode('utf-8')
|
||||
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
|
||||
expression_value = eval(f_expr, globals)
|
||||
expression_value = unsafe_eval(f_expr, globals_dict)
|
||||
if expression_value != expected_value: # assertion failed
|
||||
self.assert_report.record_assertion(False, severity)
|
||||
msg = 'assertion "%s" failed!\n' \
|
||||
@@ -682,7 +689,7 @@ form: module.record_id""" % (xml_id,)
|
||||
rec_id = rec.get("id",'').encode('ascii')
|
||||
rec_context = rec.get("context", None)
|
||||
if rec_context:
|
||||
rec_context = eval(rec_context)
|
||||
rec_context = unsafe_eval(rec_context)
|
||||
self._test_xml_id(rec_id)
|
||||
if self.isnoupdate(data_node) and self.mode != 'init':
|
||||
# check if the xml record has an id string
|
||||
@@ -722,7 +729,7 @@ form: module.record_id""" % (xml_id,)
|
||||
f_val = False
|
||||
|
||||
if f_search:
|
||||
q = eval(f_search, self.idref)
|
||||
q = unsafe_eval(f_search, self.idref)
|
||||
field = []
|
||||
assert f_model, 'Define an attribute model="..." in your .XML file !'
|
||||
f_obj = self.pool.get(f_model)
|
||||
|
||||
+1
-1
@@ -1137,7 +1137,7 @@ icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_
|
||||
'terp-face-plain','terp-folder-blue','terp-folder-green','terp-folder-orange','terp-folder-yellow',
|
||||
'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-dolar_ok!','terp-idea','terp-stock_format-default','terp-mail-','terp-mail_delete'
|
||||
])
|
||||
|
||||
def extract_zip_file(zip_file, outdirectory):
|
||||
|
||||
+237
-49
@@ -1,66 +1,254 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
# Copyright (C) 2004-2010 OpenERP s.a. (<http://www.openerp.com>).
|
||||
#
|
||||
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
|
||||
# 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.
|
||||
#
|
||||
# WARNING: This program as such is intended to be used by professional
|
||||
# programmers who take the whole responsability of assessing all potential
|
||||
# consequences resulting from its eventual inadequacies and bugs
|
||||
# End users who are looking for a ready-to-use solution with commercial
|
||||
# garantees and support are strongly adviced to contract a Free Software
|
||||
# Service Company
|
||||
# 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/>.
|
||||
#
|
||||
# This program is Free Software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
###############################################################################
|
||||
##############################################################################
|
||||
|
||||
__export_bis = {}
|
||||
import sys
|
||||
"""
|
||||
safe_eval module - methods intended to provide more restricted alternatives to
|
||||
evaluate simple and/or untrusted code.
|
||||
|
||||
def __init_ebis():
|
||||
global __export_bis
|
||||
Methods in this module are typically used as alternatives to eval() to parse
|
||||
OpenERP domain strings, conditions and expressions, mostly based on locals
|
||||
condition/math builtins.
|
||||
"""
|
||||
|
||||
_evars = [ 'abs', 'all', 'any', 'basestring' , 'bool',
|
||||
'chr', 'cmp','complex', 'dict', 'divmod', 'enumerate',
|
||||
'float', 'frozenset', 'getattr', 'hasattr', 'hash',
|
||||
'hex', 'id','int', 'iter', 'len', 'list', 'long', 'map', 'max',
|
||||
'min', 'oct', 'ord','pow', 'range', 'reduce', 'repr',
|
||||
'reversed', 'round', 'set', 'setattr', 'slice','sorted', 'str',
|
||||
'sum', 'tuple','type', 'unichr','unicode', 'xrange',
|
||||
'True','False', 'None', 'NotImplemented', 'Ellipsis', ]
|
||||
# Module partially ripped from/inspired by several different sources:
|
||||
# - http://code.activestate.com/recipes/286134/
|
||||
# - safe_eval in lp:~xrg/openobject-server/optimize-5.0
|
||||
# - safe_eval in tryton http://hg.tryton.org/hgwebdir.cgi/trytond/rev/bbb5f73319ad
|
||||
# - python 2.6's ast.literal_eval
|
||||
|
||||
if sys.version_info[0:2] >= (2,6):
|
||||
_evars.extend(['bin', 'format', 'next'])
|
||||
for v in _evars:
|
||||
__export_bis[v] = __builtins__[v]
|
||||
from opcode import HAVE_ARGUMENT, opmap, opname
|
||||
from types import CodeType
|
||||
|
||||
__all__ = ['test_expr', 'literal_eval', 'safe_eval', 'const_eval', 'ext_eval' ]
|
||||
|
||||
_CONST_OPCODES = set(opmap[x] for x in [
|
||||
'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP',
|
||||
'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE',
|
||||
'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR'] if x in opmap)
|
||||
|
||||
_EXPR_OPCODES = _CONST_OPCODES.union(set(opmap[x] for x in [
|
||||
'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
|
||||
'UNARY_INVERT', 'BINARY_POWER', 'BINARY_MULTIPLY',
|
||||
'BINARY_DIVIDE', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE',
|
||||
'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR',
|
||||
'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR',
|
||||
'BINARY_OR'] if x in opmap))
|
||||
|
||||
_SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
|
||||
'STORE_MAP', 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
|
||||
'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE',
|
||||
'DELETE_NAME', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE',
|
||||
] if x in opmap))
|
||||
|
||||
|
||||
__init_ebis()
|
||||
def _get_opcodes(codeobj):
|
||||
"""_get_opcodes(codeobj) -> [opcodes]
|
||||
|
||||
Extract the actual opcodes as a list from a code object
|
||||
|
||||
>>> c = compile("[1 + 2, (1,2)]", "", "eval")
|
||||
>>> _get_opcodes(c)
|
||||
[100, 100, 23, 100, 100, 102, 103, 83]
|
||||
"""
|
||||
i = 0
|
||||
opcodes = []
|
||||
byte_codes = codeobj.co_code
|
||||
while i < len(byte_codes):
|
||||
code = ord(byte_codes[i])
|
||||
opcodes.append(code)
|
||||
if code >= HAVE_ARGUMENT:
|
||||
i += 3
|
||||
else:
|
||||
i += 1
|
||||
return opcodes
|
||||
|
||||
def test_expr(expr, allowed_codes, mode="eval"):
|
||||
"""test_expr(expression, allowed_codes[, mode]) -> code_object
|
||||
|
||||
Test that the expression contains only the allowed opcodes.
|
||||
If the expression is valid and contains only allowed codes,
|
||||
return the compiled code object. Otherwise raise a ValueError.
|
||||
"""
|
||||
try:
|
||||
code_obj = compile(expr, "", mode)
|
||||
except:
|
||||
raise ValueError("%s is not a valid expression" % expr)
|
||||
for code in _get_opcodes(code_obj):
|
||||
if code not in allowed_codes:
|
||||
raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
|
||||
return code_obj
|
||||
|
||||
|
||||
def safe_eval(expr,sglobals,slocals = None):
|
||||
""" A little safer version of eval().
|
||||
This one, will use fewer builtin functions, so that only
|
||||
arithmetic and logic expressions can really work """
|
||||
def const_eval(expr):
|
||||
"""const_eval(expression) -> value
|
||||
|
||||
global __export_bis
|
||||
Safe Python constant evaluation
|
||||
|
||||
if not sglobals.has_key('__builtins__'):
|
||||
# we copy, because we wouldn't want successive calls to safe_eval
|
||||
# to be able to alter the builtins.
|
||||
sglobals['__builtins__'] = __export_bis.copy()
|
||||
Evaluates a string that contains an expression describing
|
||||
a Python constant. Strings that are not valid Python expressions
|
||||
or that contain other code besides the constant raise ValueError.
|
||||
|
||||
return eval(expr,sglobals,slocals)
|
||||
>>> const_eval("10")
|
||||
10
|
||||
>>> const_eval("[1,2, (3,4), {'foo':'bar'}]")
|
||||
[1, 2, (3, 4), {'foo': 'bar'}]
|
||||
>>> const_eval("1+2")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode BINARY_ADD not allowed
|
||||
"""
|
||||
c = test_expr(expr, _CONST_OPCODES)
|
||||
return eval(c)
|
||||
|
||||
#eof
|
||||
def expr_eval(expr):
|
||||
"""expr_eval(expression) -> value
|
||||
|
||||
Restricted Python expression evaluation
|
||||
|
||||
Evaluates a string that contains an expression that only
|
||||
uses Python constants. This can be used to e.g. evaluate
|
||||
a numerical expression from an untrusted source.
|
||||
|
||||
>>> expr_eval("1+2")
|
||||
3
|
||||
>>> expr_eval("[1,2]*2")
|
||||
[1, 2, 1, 2]
|
||||
>>> expr_eval("__import__('sys').modules")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode LOAD_NAME not allowed
|
||||
"""
|
||||
c = test_expr(expr, _EXPR_OPCODES)
|
||||
return eval(c)
|
||||
|
||||
|
||||
# Port of Python 2.6's ast.literal_eval for use under Python 2.5
|
||||
SAFE_CONSTANTS = {'None': None, 'True': True, 'False': False}
|
||||
|
||||
try:
|
||||
# first, try importing directly
|
||||
from ast import literal_eval
|
||||
except ImportError:
|
||||
import _ast as ast
|
||||
|
||||
def _convert(node):
|
||||
if isinstance(node, ast.Str):
|
||||
return node.s
|
||||
elif isinstance(node, ast.Num):
|
||||
return node.n
|
||||
elif isinstance(node, ast.Tuple):
|
||||
return tuple(map(_convert, node.elts))
|
||||
elif isinstance(node, ast.List):
|
||||
return list(map(_convert, node.elts))
|
||||
elif isinstance(node, ast.Dict):
|
||||
return dict((_convert(k), _convert(v)) for k, v
|
||||
in zip(node.keys, node.values))
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id in SAFE_CONSTANTS:
|
||||
return SAFE_CONSTANTS[node.id]
|
||||
raise ValueError('malformed or disallowed expression')
|
||||
|
||||
def parse(expr, filename='<unknown>', mode='eval'):
|
||||
"""parse(source[, filename], mode]] -> code object
|
||||
Parse an expression into an AST node.
|
||||
Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
|
||||
"""
|
||||
return compile(expr, filename, mode, ast.PyCF_ONLY_AST)
|
||||
|
||||
def literal_eval(node_or_string):
|
||||
"""literal_eval(expression) -> value
|
||||
Safely evaluate an expression node or a string containing a Python
|
||||
expression. The string or node provided may only consist of the
|
||||
following Python literal structures: strings, numbers, tuples,
|
||||
lists, dicts, booleans, and None.
|
||||
|
||||
>>> literal_eval('[1,True,"spam"]')
|
||||
[1, True, 'spam']
|
||||
|
||||
>>> literal_eval('1+3')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: malformed or disallowed expression
|
||||
"""
|
||||
if isinstance(node_or_string, basestring):
|
||||
node_or_string = parse(node_or_string)
|
||||
if isinstance(node_or_string, ast.Expression):
|
||||
node_or_string = node_or_string.body
|
||||
return _convert(node_or_string)
|
||||
|
||||
|
||||
|
||||
def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False):
|
||||
"""safe_eval(expression[, globals[, locals[, mode[, nocopy]]]]) -> result
|
||||
|
||||
System-restricted Python expression evaluation
|
||||
|
||||
Evaluates a string that contains an expression that mostly
|
||||
uses Python constants, arithmetic expressions and the
|
||||
objects directly provided in context.
|
||||
|
||||
This can be used to e.g. evaluate
|
||||
an OpenERP domain expression from an untrusted source.
|
||||
|
||||
>>> safe_eval("__import__('sys').modules")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode LOAD_NAME not allowed
|
||||
|
||||
"""
|
||||
if isinstance(expr, CodeType):
|
||||
raise ValueError("safe_eval does not allow direct evaluation of code objects.")
|
||||
|
||||
if '__subclasses__' in expr:
|
||||
raise ValueError('expression not allowed (__subclasses__)')
|
||||
|
||||
if globals_dict is None:
|
||||
globals_dict = {}
|
||||
|
||||
# prevent altering the globals/locals from within the sandbox
|
||||
# by taking a copy.
|
||||
if not nocopy:
|
||||
# isinstance() does not work below, we want *exactly* the dict class
|
||||
if (globals_dict is not None and type(globals_dict) is not dict) \
|
||||
or (locals_dict is not None and type(locals_dict) is not dict):
|
||||
logging.getLogger('safe_eval').warning('Looks like you are trying to pass a dynamic environment,"\
|
||||
"you should probably pass nocopy=True to safe_eval()')
|
||||
|
||||
globals_dict = dict(globals_dict)
|
||||
if locals_dict is not None:
|
||||
locals_dict = dict(locals_dict)
|
||||
|
||||
globals_dict.update(
|
||||
__builtins__ = {
|
||||
'True': True,
|
||||
'False': False,
|
||||
'None': None,
|
||||
'str': str,
|
||||
'globals': locals,
|
||||
'locals': locals,
|
||||
'bool': bool,
|
||||
'dict': dict,
|
||||
'list': list,
|
||||
'tuple': tuple,
|
||||
}
|
||||
)
|
||||
|
||||
return eval(test_expr(expr,_SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -8,9 +8,13 @@ import netsvc
|
||||
import misc
|
||||
from config import config
|
||||
import yaml_tag
|
||||
|
||||
import yaml
|
||||
|
||||
# YAML import needs both safe and unsafe eval, but let's
|
||||
# default to /safe/.
|
||||
unsafe_eval = eval
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
logger_channel = 'tests'
|
||||
|
||||
class YamlImportException(Exception):
|
||||
@@ -234,8 +238,9 @@ class YamlInterpreter(object):
|
||||
record = model.browse(self.cr, self.uid, id, context)
|
||||
for test in expressions:
|
||||
try:
|
||||
success = eval(test, self.eval_context, RecordDictWrapper(record))
|
||||
success = unsafe_eval(test, self.eval_context, RecordDictWrapper(record))
|
||||
except Exception, e:
|
||||
self.logger.debug('Exception during evaluation of !assert block in yaml_file %s.', self.filename, exc_info=True)
|
||||
raise YamlImportAbortion(e)
|
||||
if not success:
|
||||
msg = 'Assertion "%s" FAILED\ntest: %s\n'
|
||||
@@ -330,7 +335,7 @@ class YamlInterpreter(object):
|
||||
else:
|
||||
raise KeyError("Object '%s' does not contain field '%s'" % (model, field_name))
|
||||
if is_ref(expression):
|
||||
elements = self.process_ref(expression)
|
||||
elements = self.process_ref(expression, column)
|
||||
if column._type in ("many2many", "one2many"):
|
||||
value = [(6, 0, elements)]
|
||||
else: # many2one
|
||||
@@ -367,12 +372,13 @@ class YamlInterpreter(object):
|
||||
code_context = {'model': model, 'cr': self.cr, 'uid': self.uid, 'log': log, 'context': self.context}
|
||||
code_context.update({'self': model}) # remove me when no !python block test uses 'self' anymore
|
||||
try:
|
||||
code = compile(statements, self.filename, 'exec')
|
||||
eval(code, {'ref': self.get_id}, code_context)
|
||||
code_obj = compile(statements, self.filename, 'exec')
|
||||
unsafe_eval(code_obj, {'ref': self.get_id}, code_context)
|
||||
except AssertionError, e:
|
||||
self._log_assert_failure(python.severity, 'AssertionError in Python code %s: %s', python.name, e)
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.debug('Exception during evaluation of !python block in yaml_file %s.', self.filename, exc_info=True)
|
||||
raise YamlImportAbortion(e)
|
||||
else:
|
||||
self.assert_report.record(True, python.severity)
|
||||
|
||||
@@ -23,6 +23,7 @@ import sys
|
||||
import netsvc
|
||||
import osv as base
|
||||
import pooler
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class Env(dict):
|
||||
def __init__(self, cr, uid, model, ids):
|
||||
@@ -54,7 +55,7 @@ def _eval_expr(cr, ident, workitem, action):
|
||||
ret=False
|
||||
else:
|
||||
env = Env(cr, uid, model, ids)
|
||||
ret = eval(line, env)
|
||||
ret = eval(line, env, nocopy=True)
|
||||
return ret
|
||||
|
||||
def execute_action(cr, ident, workitem, activity):
|
||||
|
||||
Reference in New Issue
Block a user