sgo@tinyerp.com
2013-03-26 15:42:40 +05:30
107 changed files with 20094 additions and 13253 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ graft win32
include README
include LICENSE
include MANIFEST.in
include gunicorn.conf.py
include openerp-wsgi.py
include openerp-server
include setup*
global-exclude *pyc *~ # Exclude possible garbage from previous graft.
+2 -2
View File
@@ -52,6 +52,6 @@ Description: OpenERP Enterprise Resource Management
features are accounting (analytic and financial), stock management, sales and
purchases management, tasks automation, marketing campaigns, help desk, POS,
etc. Technical features include a distributed server, flexible workflows, an
object database, a dynamic GUI, customizable reports, and NET-RPC and XML-RPC
interfaces.
object database, a dynamic GUI, customizable reports, and an XML-RPC
interface.
+2 -1
View File
@@ -6,10 +6,11 @@ case "${1}" in
remove)
deluser --quiet --system "openerp" || true
delgroup --quiet --system --only-if-empty "openerp" || true
find /var/lib/openerp -path '/var/lib/openerp/*' ! -path '/var/lib/openerp/filestore*' -delete
;;
purge)
rm -rf /var/lib/openerp-server
rm -rf /var/lib/openerp
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-3
View File
@@ -103,9 +103,6 @@ General Options
--logfile=LOGFILE file where the server log will be stored
-n INTERFACE, --interface=INTERFACE specify the TCP IP address
-p PORT, --port=PORT specify the TCP port
--net_interface=NETINTERFACE specify the TCP IP address for netrpc
--net_port=NETPORT specify the TCP port for netrpc
--no-netrpc disable netrpc
--no-xmlrpc disable xmlrpc
-i INIT, --init=INIT init a module (use "all" for all modules)
--without-demo=WITHOUT_DEMO load demo data for a module (use "all" for all modules)
+45 -2
View File
@@ -1,6 +1,6 @@
========================================
============
Architecture
========================================
============
OpenERP as a multitenant three-tiers architecture
=================================================
@@ -180,6 +180,7 @@ OpenERP follows the MVC semantic with
Network communications and WSGI
===============================
OpenERP is an HTTP web server and may also be deployed as an WSGI-compliant
application.
@@ -207,3 +208,45 @@ As such, it can be run as a stand-alone HTTP server or embedded inside OpenERP.
The HTTP namespaces /openerp/ /object/ /common/ are reserved for the XML-RPC
layer, every module restrict it's HTTP namespace to /<name_of_the_module>/
Process model
=============
In the past, the OpenERP server was using threads to handle HTTP requests
concurrently or to process cron jobs. Using threads is still the default
behavior when running the ``openerp-server`` script but not the recommended
one: it is in fact recommended to use the ``--workers`` option.
By using the ``--workers`` option, the OpenERP server will spawn a fixed number
of processes instead of spawning a new thread for each incoming request.
This has a number of advantages:
- Processes do not suffer from CPython's Global Interpreter Lock.
- Processes can be gracefully recycled while requests are still handled by the
server.
- Resources such as CPU time and memory made available to a process can be
monitored on a per-process basis.
When using the ``--workers`` options, two types of processes may be spawned:
web process, and cron process.
.. versionadded:: 7.1
.. _longpolling-worker:
When using the ``--workers`` options, three types of processes may be spawned:
web process, and cron process, just as previsouly, but also an evented (using
gevent) web process is started. It is used for long-polling as needed by the
upcoming Instant Messaging feature. As for now, that process is listening on a
different port than the main web processes. A reverse proxy (e.g. Nginx) to
listen on a unique port, mapping all requests to the normal port, but mapping
the ``/longpolling`` route to the evented process is necessary (the web
interface cannot issue requests to different ports).
(It is possible to make the threaded server evented by passing the ``--gevent``
flag.)
The goal is to drop support for the threaded model, and also make all web
processes evented; there would be no more distinction between "normal" and
"longpolling" processes. For this to happen, further testing is needed.
+9 -7
View File
@@ -6,13 +6,15 @@ Changelog
`trunk`
-------
- Removed support for the ``NET-RPC`` protocol.
- Added the :ref:`Long polling <longpolling-worker>` worker type.
- Added :ref:`orm-workflows` to the ORM.
- Added :ref:`routing-decorators` to the RPC and WSGI stack.
- Removed support for `__terp__.py` descriptor files.
- Removed support for `<terp>` root element in XML files.
- Removed support for the non-openerp namespace (e.g. importing `tools` instead
of `openerp.tools` in an addons).
- Removed support for ``__terp__.py`` descriptor files.
- Removed support for ``<terp>`` root element in XML files.
- Removed support for the non-openerp namespace (e.g. importing ``tools``
instead of ``openerp.tools`` in an addons).
- Add a new type of exception that allows redirections:
openerp.exceptions.RedirectWarning.
- Give a pair of new methods to res.config.settings and a helper to make them
easier to use: get_config_warning()
``openerp.exceptions.RedirectWarning``.
- Give a pair of new methods to ``res.config.settings`` and a helper to make
them easier to use: ``get_config_warning()``.
-11
View File
@@ -80,17 +80,6 @@ specify the certificate file for the SSL connection
\fB\-\-pkey\-file\fR=\fISECURE_PKEY_FILE\fR
specify the private key file for the SSL connection
.IP
NET\-RPC Configuration:
.TP
\fB\-\-netrpc\-interface\fR=\fINETRPC_INTERFACE\fR
specify the TCP IP address for the NETRPC protocol
.TP
\fB\-\-netrpc\-port\fR=\fINETRPC_PORT\fR
specify the TCP port for the NETRPC protocol
.TP
\fB\-\-no\-netrpc\fR
disable the NETRPC protocol
.IP
Static HTTP service:
.TP
\fB\-\-static\-http\-enable\fR
+1 -1
View File
@@ -13,6 +13,6 @@ Description: OpenERP is a complete ERP and CRM. The main features are accounting
and financial), stock management, sales and purchases management, tasks
automation, marketing campaigns, help desk, POS, etc. Technical features include
a distributed server, flexible workflows, an object database, a dynamic GUI,
customizable reports, and NET-RPC and XML-RPC interfaces.
customizable reports, and an XML-RPC interface.
Keywords: ERP, Accounting, Stock, CRM, Enterprise, Logistics, Management, Sales, Purchases
Platform: Linux, Win32
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -207,7 +207,7 @@ class act_window(osv.osv):
def _search_view(self, cr, uid, ids, name, arg, context=None):
res = {}
for act in self.browse(cr, uid, ids, context=context):
field_get = self.pool.get(act.res_model).fields_view_get(cr, uid,
field_get = self.pool[act.res_model].fields_view_get(cr, uid,
act.search_view_id and act.search_view_id.id or False,
'search', context=context)
res[act.id] = str(field_get)
@@ -792,7 +792,7 @@ Launch Manually Once: after having been launched manually, it sets automatically
act_type = self.pool.get('ir.actions.actions').read(cr, uid, wizard.action_id.id, ['type'], context=context)
res = self.pool.get(act_type['type']).read(cr, uid, wizard.action_id.id, [], context=context)
if act_type<>'ir.actions.act_window':
if act_type['type'] != 'ir.actions.act_window':
return res
res.setdefault('context','{}')
res['nodestroy'] = True
+14 -13
View File
@@ -120,11 +120,13 @@ class ir_cron(osv.osv):
:param args: arguments of the method (without the usual self, cr, uid).
:param job_id: job id.
"""
args = str2tuple(args)
model = self.pool.get(model_name)
if model and hasattr(model, method_name):
method = getattr(model, method_name)
try:
try:
args = str2tuple(args)
openerp.modules.registry.RegistryManager.check_registry_signaling(cr.dbname)
registry = openerp.pooler.get_pool(cr.dbname)
model = registry.get(model_name)
if model and hasattr(model, method_name):
method = getattr(model, method_name)
log_depth = (None if _logger.isEnabledFor(logging.DEBUG) else 1)
netsvc.log(_logger, logging.DEBUG, 'cron.object.execute', (cr.dbname,uid,'*',model_name,method_name)+tuple(args), depth=log_depth)
if _logger.isEnabledFor(logging.DEBUG):
@@ -133,12 +135,13 @@ class ir_cron(osv.osv):
if _logger.isEnabledFor(logging.DEBUG):
end_time = time.time()
_logger.debug('%.3fs (%s, %s)' % (end_time - start_time, model_name, method_name))
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
else:
msg = "Method `%s.%s` do not exist." % (model._name, method_name) \
if model else "Model `%s` do not exist." % model._name
_logger.warning(msg)
openerp.modules.registry.RegistryManager.signal_caches_change(cr.dbname)
else:
msg = "Method `%s.%s` does not exist." % (model_name, method_name) \
if model else "Model `%s` does not exist." % model_name
_logger.warning(msg)
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
def _process_job(self, job_cr, job, cron_cr):
""" Run a given job taking care of the repetition.
@@ -220,10 +223,8 @@ class ir_cron(osv.osv):
_logger.debug('Starting job `%s`.', job['name'])
job_cr = db.cursor()
try:
openerp.modules.registry.RegistryManager.check_registry_signaling(db_name)
registry = openerp.pooler.get_pool(db_name)
registry[cls._name]._process_job(job_cr, job, lock_cr)
openerp.modules.registry.RegistryManager.signal_caches_change(db_name)
except Exception:
_logger.exception('Unexpected exception while processing cron job %r', job)
finally:
+22 -14
View File
@@ -933,13 +933,6 @@ class ir_model_data(osv.osv):
elif res_id:
model_obj.write(cr, uid, [res_id], values, context=context)
if xml_id:
self.create(cr, uid, {
'name': xml_id,
'model': model,
'module':module,
'res_id':res_id,
'noupdate': noupdate,
},context=context)
if model_obj._inherits:
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
@@ -951,17 +944,17 @@ class ir_model_data(osv.osv):
'res_id': inherit_id.id,
'noupdate': noupdate,
},context=context)
self.create(cr, uid, {
'name': xml_id,
'model': model,
'module':module,
'res_id':res_id,
'noupdate': noupdate,
},context=context)
else:
if mode=='init' or (mode=='update' and xml_id):
res_id = model_obj.create(cr, uid, values, context=context)
if xml_id:
self.create(cr, uid, {
'name': xml_id,
'model': model,
'module': module,
'res_id': res_id,
'noupdate': noupdate
},context=context)
if model_obj._inherits:
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
@@ -973,6 +966,13 @@ class ir_model_data(osv.osv):
'res_id': inherit_id.id,
'noupdate': noupdate,
},context=context)
self.create(cr, uid, {
'name': xml_id,
'model': model,
'module': module,
'res_id': res_id,
'noupdate': noupdate
},context=context)
if xml_id and res_id:
self.loads[(module, xml_id)] = (model, res_id)
for table, inherit_field in model_obj._inherits.iteritems():
@@ -1060,6 +1060,14 @@ class ir_model_data(osv.osv):
if set(external_ids)-ids_set:
# if other modules have defined this record, we must not delete it
continue
if model == 'ir.model.fields':
# Don't remove the LOG_ACCESS_COLUMNS unless _log_access
# has been turned off on the model.
field = self.pool.get(model).browse(cr, uid, [res_id], context=context)[0]
if field.name in openerp.osv.orm.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
continue
if field.name == 'id':
continue
_logger.info('Deleting %s@%s', res_id, model)
try:
cr.execute('SAVEPOINT record_unlink_save')
+14 -3
View File
@@ -557,7 +557,10 @@ class res_config_settings(osv.osv_memory):
'params': {'modules': to_install_missing_names},
}
config = self.pool.get('res.config').next(cr, uid, [], context=context) or {}
# After the uninstall/install calls, the self.pool is no longer valid.
# So we reach into the RegistryManager directly.
res_config = openerp.modules.registry.RegistryManager.get(cr.dbname)['res.config']
config = res_config.next(cr, uid, [], context=context) or {}
if config.get('type') not in ('ir.actions.act_window_close',):
return config
@@ -612,7 +615,7 @@ class res_config_settings(osv.osv_memory):
"""
Fetch the human readable name of a specified configuration option.
:param string full_field_name: the full name of the field, structured as follows:
:param string full_field_name: the full name of the field, structured as follows:
model_name.field_name (e.g.: "sale.config.settings.fetchmail_lead")
:return string: human readable name of the field (e.g.: "Create leads from incoming mails")
"""
@@ -622,7 +625,7 @@ class res_config_settings(osv.osv_memory):
def get_config_warning(self, cr, msg, context=None):
"""
Helper: return a Warning exception with the given message where the %(field:xxx)s
Helper: return a Warning exception with the given message where the %(field:xxx)s
and/or %(menu:yyy)s are replaced by the human readable field's name and/or menuitem's
full path.
@@ -639,6 +642,14 @@ class res_config_settings(osv.osv_memory):
This will return an exception containing the following message:
Error: this action is prohibited. You should check the field Create leads from incoming mails in Settings/Configuration/Sales.
What if there is another substitution in the message already?
-------------------------------------------------------------
You could have a situation where the error message you want to upgrade already contains a substitution. Example:
Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals.
What you want to do here is simply to replace the path by %menu:account.menu_account_config)s, and leave the rest alone.
In order to do that, you can use the double percent (%%) to escape your new substitution, like so:
Cannot find any account journal of %s type for this company.\n\nYou can create one in the %%(menu:account.menu_account_config)s.
"""
res_config_obj = pooler.get_pool(cr.dbname).get('res.config.settings')
+1 -1
View File
@@ -77,7 +77,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
context=context)
def write(self, cursor, user, ids, vals, context=None):
if 'code' in vals:
if vals.get('code', False):
vals['code'] = vals['code'].upper()
return super(Country, self).write(cursor, user, ids, vals,
context=context)
+4 -10
View File
@@ -362,20 +362,14 @@ class res_partner(osv.osv, format_address):
# Update parent and siblings or children records
if isinstance(ids, (int, long)):
ids = [ids]
if vals.get('is_company')==False:
vals.update({'child_ids' : [(5,)]})
for partner in self.browse(cr, uid, ids, context=context):
update_ids = []
if partner.is_company:
domain_children = [('parent_id', '=', partner.id), ('use_parent_address', '=', True)]
domain_children = [('parent_id', 'child_of', partner.id), ('use_parent_address', '=', True)]
update_ids = self.search(cr, uid, domain_children, context=context)
elif partner.parent_id:
if vals.get('use_parent_address')==True:
domain_siblings = [('parent_id', '=', partner.parent_id.id), ('use_parent_address', '=', True)]
update_ids = [partner.parent_id.id] + self.search(cr, uid, domain_siblings, context=context)
if 'use_parent_address' not in vals and partner.use_parent_address:
domain_siblings = [('parent_id', '=', partner.parent_id.id), ('use_parent_address', '=', True)]
update_ids = [partner.parent_id.id] + self.search(cr, uid, domain_siblings, context=context)
elif partner.parent_id and vals.get('use_parent_address', partner.use_parent_address):
domain_siblings = [('parent_id', '=', partner.parent_id.id), ('use_parent_address', '=', True)]
update_ids = [partner.parent_id.id] + self.search(cr, uid, domain_siblings, context=context)
self.update_address(cr, uid, update_ids, vals, context)
return super(res_partner,self).write(cr, uid, ids, vals, context=context)
+1 -1
View File
@@ -87,7 +87,7 @@
<field name="city">Wavre</field>
<field name="zip">1300</field>
<field name="country_id" ref="base.be"/>
<field name="street">69 rue de Chimay</field>
<field name="street">69 rue de Namur</field>
<field name="email">info@agrolait.com</field>
<field name="phone">+32 10 588 558</field>
<field name="website">www.agrolait.com</field>
+2 -1
View File
@@ -269,7 +269,8 @@
<page string="Sales &amp; Purchases">
<group>
<group>
<field name="user_id"/>
<field name="user_id"
context="{'default_groups_ref': ['base.group_partner_manager']}"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
<group>
+16
View File
@@ -744,6 +744,7 @@ class users_view(osv.osv):
def create(self, cr, uid, values, context=None):
self._set_reified_groups(values)
return super(users_view, self).create(cr, uid, values, context)
def write(self, cr, uid, ids, values, context=None):
@@ -782,6 +783,21 @@ class users_view(osv.osv):
fields1 = (fields + ['groups_id']) if group_fields else fields
values = super(users_view, self).default_get(cr, uid, fields1, context)
self._get_reified_groups(group_fields, values)
# add "default_groups_ref" inside the context to set default value for group_id with xml values
if 'groups_id' in fields and isinstance(context.get("default_groups_ref"), list):
groups = []
ir_model_data = self.pool.get('ir.model.data')
for group_xml_id in context["default_groups_ref"]:
group_split = group_xml_id.split('.')
if len(group_split) != 2:
raise osv.except_osv(_('Invalid context value'), _('Invalid context default_groups_ref value (model.name_id) : "%s"') % group_xml_id)
try:
temp, group_id = ir_model_data.get_object_reference(cr, uid, group_split[0], group_split[1])
except ValueError:
group_id = False
groups += [group_id]
values['groups_id'] = groups
return values
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
+43 -48
View File
@@ -84,48 +84,39 @@
</record>
<menuitem action="action_res_groups" id="menu_action_res_groups" parent="base.menu_users" groups="base.group_no_one"/>
<!-- res.users -->
<record id="view_users_simple_form" model="ir.ui.view">
<field name="name">res.users.simplified.form</field>
<field name="model">res.users</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<form string="Contact" version="7.0">
<form string="Users" version="7.0">
<sheet>
<field name="image" widget='image' class="oe_left oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name"/>
<field name="id" invisible="1"/>
<div class="oe_form_box_info oe_text_center" style="margin-bottom: 10px" attrs="{'invisible': [('id', '>', 0)]}">
You are creating a new user. After saving, the user will receive an invite email containing a link to set its password.
</div>
<field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
<label for="login" class="oe_edit_only"/>
<h2><field name="login"/></h2>
<label for="company_id" class="oe_edit_only" groups="base.group_multi_company"/>
<field name="company_id" context="{'user_preference': 0}" groups="base.group_multi_company"/>
</div>
<h1>
<field name="name" default_focus="1" placeholder="Name" />
</h1>
<field name="category_id" widget="many2many_tags" placeholder="Tags..."/>
</div>
<group>
<group>
<label for="street" string="Address"/>
<div>
<field name="street" placeholder="Street..."/>
<field name="street2"/>
<div class="address_format">
<field name="city" placeholder="City" style="width: 40%%"/>
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)"/>
<field name="zip" placeholder="ZIP" style="width: 20%%"/>
</div>
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}'/>
<label for="groups_id" string="Access Rights"
attrs="{'invisible': [('id', '>', 0)]}"/>
<div attrs="{'invisible': [('id', '>', 0)]}">
<field name="groups_id" readonly="1" widget="many2many_tags" style="display: inline;"/> You will be able to define additional access rights by editing the newly created user under the Settings / Users menu.
</div>
<field name="website" widget="url" placeholder="e.g. www.openerp.com"/>
</group>
<group>
<field name="phone" placeholder="e.g. +32.81.81.37.00"/>
<field name="mobile"/>
<field name="fax"/>
<field name="email" widget="email"/>
<field name="email" widget="email" required="True"/>
</group>
</group>
</sheet>
</sheet>
</form>
</field>
</record>
@@ -134,8 +125,8 @@
<field name="model">res.users</field>
<field name="arch" type="xml">
<form string="Users" version="7.0">
<field name="id" invisible="1"/>
<sheet>
<field name="id" invisible="1"/>
<div class="oe_right oe_button_box">
<button string="Change Password" type="action" name="%(change_password_wizard_action)d"
help="Change the user password."/>
@@ -146,17 +137,29 @@
<h1><field name="name"/></h1>
<label for="login" class="oe_edit_only"/>
<h2><field name="login"/></h2>
<label for="company_id" class="oe_edit_only" groups="base.group_multi_company"/>
<field name="company_id" context="{'user_preference': 0}" groups="base.group_multi_company"/>
</div>
<group>
<group>
<field name="company_id" context="{'user_preference': 0}"/>
</group>
<group>
<field name="active"/>
</group>
</group>
<notebook colspan="4">
<page string="Access Rights">
<group>
<field name="active"/>
</group>
<group string="Allowed Companies" groups="base.group_multi_company">
<field name="company_ids" nolabel="1" widget="many2many_tags"/>
</group>
<group col="4">
<field name="groups_id"/>
</group>
</page>
<page string="Contact">
<group>
<field name="phone" placeholder="e.g. +32.81.81.37.00"/>
<field name="mobile"/>
<field name="fax"/>
<field name="email" widget="email"/>
</group>
</page>
<page string="Preferences">
<group>
<group name="preferences">
@@ -168,24 +171,16 @@
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
</group>
</group>
<group>
<field name="email" widget="email"/>
<group string="Email preferences">
<field name="signature"/>
</group>
</page>
<page string="Access Rights">
<group string="Allowed Companies" groups="base.group_multi_company">
<field name="company_ids" nolabel="1" widget="many2many_tags"/>
</group>
<group col="4">
<field name="groups_id"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_users_tree" model="ir.ui.view">
<field name="name">res.users.tree</field>
<field name="model">res.users</field>
+1
View File
@@ -393,6 +393,7 @@
<rng:optional><rng:attribute name="day_length"/></rng:optional>
<rng:optional><rng:attribute name="date_start"/></rng:optional>
<rng:optional><rng:attribute name="date_stop"/></rng:optional>
<rng:optional><rng:attribute name="progress"/></rng:optional>
<rng:optional><rng:attribute name="date_string"/></rng:optional>
<rng:optional><rng:attribute name="string"/></rng:optional>
<rng:optional><rng:attribute name="default_group_by"/></rng:optional>
+1 -1
View File
@@ -47,7 +47,7 @@
with mute_logger('openerp.tools.safe_eval'):
safe_eval('open("/etc/passwd","r")')
assert False, "safe_eval should not allow calling open() builtin"
except NameError:
except ValueError:
pass
-
+13 -13
View File
@@ -39,42 +39,42 @@ class test_res_config(common.TransactionCase):
res = self.res_config.get_option_path(self.cr, self.uid, self.menu_xml_id, context=None)
# Check types
self.assertTrue(isinstance(res, tuple)), "The result of get_option_path() should be a tuple (got %s)" % type(res)
self.assertTrue(len(res) == 2), "The tuple should contain 2 elements (got %s)" % len(res)
self.assertTrue(isinstance(res[0], basestring)), "The first element of the tuple should be a string (got %s)" % type(res[0])
self.assertTrue(isinstance(res[1], long)), "The second element of the tuple should be an long (got %s)" % type(res[1])
self.assertIsInstance(res, tuple)
self.assertEqual(len(res), 2, "The result should contain 2 elements")
self.assertIsInstance(res[0], basestring)
self.assertIsInstance(res[1], long)
# Check returned values
self.assertTrue(res[0] == self.expected_path), "Result mismatch: expected %s, got %s" % (self.expected_path, res[0])
self.assertTrue(res[1] == self.expected_action_id), "Result mismatch: expected %s, got %s" % (self.expected_action_id, res[1])
self.assertEqual(res[0], self.expected_path)
self.assertEqual(res[1], self.expected_action_id)
def test_10_get_option_name(self):
""" The get_option_name() method should return a string """
res = self.res_config.get_option_name(self.cr, self.uid, self.full_field_name, context=None)
# Check type
self.assertTrue(isinstance(res, basestring)), "Result type mismatch: expected basestring, got %s" % type(res)
self.assertIsInstance(res, basestring)
# Check returned value
self.assertTrue(res == self.expected_name), "Result mismatch: expected %s, got %s" % (self.expected_name, res)
self.assertEqual(res, self.expected_name)
def test_20_get_config_warning(self):
""" The get_config_warning() method should return a RedirectWarning """
res = self.res_config.get_config_warning(self.cr, self.error_msg, context=None)
# Check type
self.assertTrue(isinstance(res, openerp.exceptions.RedirectWarning)), "Result type mismatch: expected openerp.exceptions.RedirectWarning, got %s" % type(res)
self.assertIsInstance(res, openerp.exceptions.RedirectWarning)
# Check returned value
self.assertTrue(res.args[0], self.expected_final_error_msg), "Result mismatch: expected %s, got %s" % (self.expected_final_error_msg, res.args[0])
self.assertTrue(res.args[1], self.expected_action_id), "Result mismatch: expected %s, got %s" % (self.expected_action_id, res.args[1])
self.assertEqual(res.args[0], self.expected_final_error_msg)
self.assertEqual(res.args[1], self.expected_action_id)
def test_30_get_config_warning_wo_menu(self):
""" The get_config_warning() method should return a Warning exception """
res = self.res_config.get_config_warning(self.cr, self.error_msg_wo_menu, context=None)
# Check type
self.assertTrue(isinstance(res, openerp.exceptions.Warning)), "Result type mismatch: expected openerp.exceptions.Warning, got %s" % type(res)
self.assertIsInstance(res, openerp.exceptions.Warning)
# Check returned value
self.assertTrue(res.args[0], self.expected_final_error_msg), "Result mismatch: expected %s, got %s" % (self.expected_final_error_msg_wo_menu, res.args[0])
self.assertEqual(res.args[0], self.expected_final_error_msg_wo_menu)
+8
View File
@@ -230,6 +230,14 @@ def main(args):
check_root_user()
openerp.tools.config.parse_config(args)
if openerp.tools.config.options["gevent"]:
openerp.evented = True
_logger.info('Using gevent mode')
import gevent.monkey
gevent.monkey.patch_all()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()
check_postgres_user()
openerp.netsvc.init_logger()
report_configuration()
-56
View File
@@ -62,23 +62,6 @@ class AddonsImportHook(object):
backward compatibility, `import <module>` is still supported. Now they
are living in `openerp.addons`. The good way to import such modules is
thus `import openerp.addons.module`.
For backward compatibility, loading an addons puts it in `sys.modules`
under both the legacy (short) name, and the new (longer) name. This
ensures that
import hr
import openerp.addons.hr
loads the hr addons only once.
When an OpenERP addons name clashes with some other installed Python
module (for instance this is the case of the `resource` addons),
obtaining the OpenERP addons is only possible with the long name. The
short name will give the expected Python module.
Instead of relying on some addons path, an alternative approach would be
to use pkg_resources entry points from already installed Python libraries
(and install our addons as such). Even when implemented, we would still
have to support the addons path approach for backward compatibility.
"""
def find_module(self, module_name, package_path):
@@ -86,25 +69,6 @@ class AddonsImportHook(object):
if len(module_parts) == 3 and module_name.startswith('openerp.addons.'):
return self # We act as a loader too.
# TODO list of loadable modules can be cached instead of always
# calling get_module_path().
if len(module_parts) == 1 and \
get_module_path(module_parts[0],
display_warning=False):
try:
# Check if the bare module name clashes with another module.
f, path, descr = imp.find_module(module_parts[0])
_logger.warning("""
Ambiguous import: the OpenERP module `%s` is shadowed by another
module (available at %s).
To import it, use `import openerp.addons.<module>.`.""" % (module_name, path))
return
except ImportError, e:
# Using `import <module_name>` instead of
# `import openerp.addons.<module_name>` is ugly but not harmful
# and kept for backward compatibility.
return self # We act as a loader too.
def load_module(self, module_name):
module_parts = module_name.split('.')
@@ -113,29 +77,9 @@ To import it, use `import openerp.addons.<module>.`.""" % (module_name, path))
if module_name in sys.modules:
return sys.modules[module_name]
if len(module_parts) == 1:
module_part = module_parts[0]
if module_part in sys.modules:
return sys.modules[module_part]
try:
# Check if the bare module name shadows another module.
f, path, descr = imp.find_module(module_part)
is_shadowing = True
except ImportError, e:
# Using `import <module_name>` instead of
# `import openerp.addons.<module_name>` is ugly but not harmful
# and kept for backward compatibility.
is_shadowing = False
# Note: we don't support circular import.
f, path, descr = imp.find_module(module_part, ad_paths)
mod = imp.load_module('openerp.addons.' + module_part, f, path, descr)
if not is_shadowing:
sys.modules[module_part] = mod
for k in sys.modules.keys():
if k.startswith('openerp.addons.' + module_part):
sys.modules[k[len('openerp.addons.'):]] = sys.modules[k]
sys.modules['openerp.addons.' + module_part] = mod
return mod
+6 -2
View File
@@ -205,8 +205,8 @@ def log(logger, level, prefix, msg, depth=None):
def dispatch_rpc(service_name, method, params):
""" Handle a RPC call.
This is pure Python code, the actual marshalling (from/to XML-RPC or
NET-RPC) is done in a upper layer.
This is pure Python code, the actual marshalling (from/to XML-RPC) is done
in a upper layer.
"""
try:
rpc_request = logging.getLogger(__name__ + '.rpc.request')
@@ -247,12 +247,16 @@ def dispatch_rpc(service_name, method, params):
log(rpc_request,logging.DEBUG, logline, replace_request_password(params), depth=1)
return result
except openerp.osv.orm.except_orm:
raise
except openerp.exceptions.AccessError:
raise
except openerp.exceptions.AccessDenied:
raise
except openerp.exceptions.Warning:
raise
except openerp.exceptions.RedirectWarning:
raise
except openerp.exceptions.DeferredException, e:
_logger.exception(tools.exception_to_unicode(e))
post_mortem(e.traceback)
+47 -44
View File
@@ -59,7 +59,6 @@ from lxml import etree
import fields
import openerp
import openerp.netsvc as netsvc
import openerp.tools as tools
from openerp.tools.config import config
from openerp.tools.misc import CountingStream
@@ -481,7 +480,9 @@ class browse_record(object):
try:
return self[name]
except KeyError, e:
raise AttributeError(e)
import sys
exc_info = sys.exc_info()
raise AttributeError, "Got %r while trying to get attribute %s on a %s record." % (e, name, self._table._name), exc_info[2]
def __contains__(self, name):
return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name)
@@ -3590,8 +3591,6 @@ class BaseModel(object):
"""
if not context:
context = {}
self.check_access_rights(cr, user, 'read')
fields = self.check_field_access_rights(cr, user, 'read', fields)
if isinstance(ids, (int, long)):
@@ -3601,12 +3600,7 @@ class BaseModel(object):
select = map(lambda x: isinstance(x, dict) and x['id'] or x, select)
result = self._read_flat(cr, user, select, fields, context, load)
for r in result:
for key, v in r.items():
if v is None:
r[key] = False
if isinstance(ids, (int, long, dict)):
if isinstance(ids, (int, long)):
return result and result[0] or False
return result
@@ -3740,34 +3734,37 @@ class BaseModel(object):
if field in self._columns:
fobj = self._columns[field]
if not fobj:
continue
groups = fobj.read
if groups:
edit = False
for group in groups:
module = group.split(".")[0]
grp = group.split(".")[1]
cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \
(grp, module, 'res.groups', user))
readonly = cr.fetchall()
if readonly[0][0] >= 1:
edit = True
break
elif readonly[0][0] == 0:
edit = False
else:
edit = False
if fobj:
groups = fobj.read
if groups:
edit = False
for group in groups:
module = group.split(".")[0]
grp = group.split(".")[1]
cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \
(grp, module, 'res.groups', user))
readonly = cr.fetchall()
if readonly[0][0] >= 1:
edit = True
break
elif readonly[0][0] == 0:
edit = False
else:
edit = False
if not edit:
if type(vals[field]) == type([]):
vals[field] = []
elif type(vals[field]) == type(0.0):
vals[field] = 0
elif type(vals[field]) == type(''):
vals[field] = '=No Permission='
else:
vals[field] = False
if vals[field] is None:
vals[field] = False
if not edit:
if type(vals[field]) == type([]):
vals[field] = []
elif type(vals[field]) == type(0.0):
vals[field] = 0
elif type(vals[field]) == type(''):
vals[field] = '=No Permission='
else:
vals[field] = False
return res
# TODO check READ access
@@ -4870,12 +4867,16 @@ class BaseModel(object):
limit_str = limit and ' limit %d' % limit or ''
offset_str = offset and ' offset %d' % offset or ''
where_str = where_clause and (" WHERE %s" % where_clause) or ''
query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
if count:
cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params)
res = cr.fetchall()
return res[0][0]
cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)
# /!\ the main query must be executed as a subquery, otherwise
# offset and limit apply to the result of count()!
cr.execute('SELECT count(*) FROM (%s) AS count' % query_str, where_clause_params)
res = cr.fetchone()
return res[0]
cr.execute(query_str, where_clause_params)
res = cr.fetchall()
# TDE note: with auto_join, we could have several lines about the same result
@@ -5277,7 +5278,10 @@ class BaseModel(object):
assert signal_name
return (lambda *args, **kwargs:
self.signal_workflow(*args, signal=signal_name, **kwargs))
return super(BaseModel, self).__getattr__(name)
get = getattr(super(BaseModel, self), '__getattr__', None)
if get is not None: return get(name)
raise AttributeError(
"'%s' object has no attribute '%s'" % (type(self).__name__, name))
# keep this import here, at top it will cause dependency cycle errors
import expression
@@ -5349,8 +5353,7 @@ def convert_pgerror_23502(model, fields, info, e):
message = _(u"Missing required value for the field '%s'.") % field_name
field = fields.get(field_name)
if field:
message = _(u"%s This might be '%s' in the current model, or a field "
u"of the same name in an o2m.") % (message, field['string'])
message = _(u"Missing required value for the field '%s' (%s)") % (field['string'], field_name)
return {
'message': message,
'field': field_name,
+5 -2
View File
@@ -31,6 +31,7 @@ import time
import cron
import wsgi_server
import openerp
import openerp.modules
import openerp.netsvc
import openerp.osv
@@ -85,12 +86,14 @@ def start_services():
# Start the WSGI server.
wsgi_server.start_service()
# Start the main cron thread.
cron.start_service()
if not openerp.evented:
cron.start_service()
def stop_services():
""" Stop all services. """
# stop services
cron.stop_service()
if not openerp.evented:
cron.stop_service()
wsgi_server.stop_service()
_logger.info("Initiating shutdown")
+1 -5
View File
@@ -44,11 +44,7 @@ def cron_runner(number):
_logger.debug('cron%d polling for jobs', number)
for db_name, registry in registries.items():
while True and registry.ready:
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# TODO why isnt openerp.addons.base defined ?
import sys
base = sys.modules['addons.base']
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
if not acquired:
break
+30 -22
View File
@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
import base64
import contextlib
import logging
import os
import threading
import traceback
from openerp import SUPERUSER_ID
import openerp.pooler
import openerp.release
import openerp.sql_db
import openerp.tools
@@ -20,39 +23,44 @@ self_id_protect = threading.Semaphore()
# This should be moved to openerp.modules.db, along side initialize().
def _initialize_db(id, db_name, demo, lang, user_password):
cr = None
try:
self_actions[id]['progress'] = 0
cr = openerp.sql_db.db_connect(db_name).cursor()
openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by pooler.restart_pool.
openerp.tools.config['lang'] = lang
cr.commit()
cr.close()
cr = None
try:
self_actions[id]['progress'] = 0
cr = openerp.sql_db.db_connect(db_name).cursor()
openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by pooler.restart_pool.
openerp.tools.config['lang'] = lang
cr.commit()
finally:
if cr:
cr.close()
cr = None
pool = openerp.pooler.restart_pool(db_name, demo, self_actions[id],
update_module=True)[1]
cr = openerp.sql_db.db_connect(db_name).cursor()
try:
cr = openerp.sql_db.db_connect(db_name).cursor()
if lang:
modobj = pool.get('ir.module.module')
mids = modobj.search(cr, SUPERUSER_ID, [('state', '=', 'installed')])
modobj.update_translations(cr, SUPERUSER_ID, mids, lang)
if lang:
modobj = pool.get('ir.module.module')
mids = modobj.search(cr, SUPERUSER_ID, [('state', '=', 'installed')])
modobj.update_translations(cr, SUPERUSER_ID, mids, lang)
# update admin's password and lang
values = {'password': user_password, 'lang': lang}
pool.get('res.users').write(cr, SUPERUSER_ID, [SUPERUSER_ID], values)
# update admin's password and lang
values = {'password': user_password, 'lang': lang}
pool.get('res.users').write(cr, SUPERUSER_ID, [SUPERUSER_ID], values)
cr.execute('SELECT login, password FROM res_users ORDER BY login')
self_actions[id].update(users=cr.dictfetchall(), clean=True)
cr.commit()
cr.close()
cr.execute('SELECT login, password FROM res_users ORDER BY login')
self_actions[id].update(users=cr.dictfetchall(), clean=True)
cr.commit()
finally:
if cr:
cr.close()
except Exception, e:
self_actions[id].update(clean=False, exception=e)
_logger.exception('CREATE DATABASE failed:')
self_actions[id]['traceback'] = traceback.format_exc()
if cr:
cr.close()
def dispatch(method, params):
if method in [ 'create', 'get_progress', 'drop', 'dump',
@@ -337,7 +345,7 @@ def exp_server_version():
""" Return the version of the server
Used by the client to verify the compatibility with its own version
"""
return release.version
return openerp.release.version
def exp_migrate_databases(databases):
for db in databases:
-5
View File
@@ -98,8 +98,6 @@ def check(f):
if openerp.pooler.get_pool(dbname)._init:
raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.')
return f(dbname, *args, **kwargs)
except except_orm:
raise
except IntegrityError, inst:
osv_pool = openerp.pooler.get_pool(dbname)
for key in osv_pool._sql_error.keys():
@@ -127,9 +125,6 @@ def check(f):
raise openerp.osv.orm.except_orm(_('Integrity Error'), msg)
else:
raise openerp.osv.orm.except_orm(_('Integrity Error'), inst[0])
except Exception:
_logger.exception("Uncaught exception")
raise
return wrapper
+34 -2
View File
@@ -35,6 +35,7 @@ class Multicorn(object):
def __init__(self, app):
# config
self.address = (config['xmlrpc_interface'] or '0.0.0.0', config['xmlrpc_port'])
self.long_polling_address = (config['xmlrpc_interface'] or '0.0.0.0', config['longpolling_port'])
self.population = config['workers']
self.timeout = config['limit_time_real']
self.limit_request = config['limit_request']
@@ -45,6 +46,7 @@ class Multicorn(object):
self.socket = None
self.workers_http = {}
self.workers_cron = {}
self.workers_longpolling = {}
self.workers = {}
self.generation = 0
self.queue = []
@@ -131,7 +133,8 @@ class Multicorn(object):
def process_timeout(self):
now = time.time()
for (pid, worker) in self.workers.items():
if now - worker.watchdog_time >= worker.watchdog_timeout:
if (worker.watchdog_timeout is not None) and \
(now - worker.watchdog_time >= worker.watchdog_timeout):
_logger.error("Worker (%s) timeout", pid)
self.worker_kill(pid, signal.SIGKILL)
@@ -140,6 +143,8 @@ class Multicorn(object):
self.worker_spawn(WorkerHTTP, self.workers_http)
while len(self.workers_cron) < config['max_cron_threads']:
self.worker_spawn(WorkerCron, self.workers_cron)
while len(self.workers_longpolling) < 1:
self.worker_spawn(WorkerLongPolling, self.workers_longpolling)
def sleep(self):
try:
@@ -178,6 +183,12 @@ class Multicorn(object):
self.socket.setblocking(0)
self.socket.bind(self.address)
self.socket.listen(8)
# long polling socket
self.long_polling_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.long_polling_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.long_polling_socket.setblocking(0)
self.long_polling_socket.bind(self.long_polling_address)
self.long_polling_socket.listen(8)
def stop(self, graceful=True):
if graceful:
@@ -221,6 +232,7 @@ class Worker(object):
self.multi = multi
self.watchdog_time = time.time()
self.watchdog_pipe = multi.pipe_new()
# Can be set to None if no watchdog is desired.
self.watchdog_timeout = multi.timeout
self.ppid = os.getpid()
self.pid = None
@@ -340,6 +352,26 @@ class WorkerHTTP(Worker):
Worker.start(self)
self.server = WorkerBaseWSGIServer(self.multi.app)
class WorkerLongPolling(Worker):
""" Long polling workers """
def __init__(self, multi):
super(WorkerLongPolling, self).__init__(multi)
# Disable the watchdog feature for this kind of worker.
self.watchdog_timeout = None
def start(self):
openerp.evented = True
_logger.info('Using gevent mode')
import gevent.monkey
gevent.monkey.patch_all()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()
Worker.start(self)
from gevent.wsgi import WSGIServer
self.server = WSGIServer(self.multi.long_polling_socket, self.multi.app)
self.server.serve_forever()
class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
""" werkzeug WSGI Server patched to allow using an external listen socket
"""
@@ -375,7 +407,7 @@ class WorkerCron(Worker):
while True:
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# TODO why isnt openerp.addons.base defined ?
import base
import openerp.addons.base as base
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
if not acquired:
break
+13 -6
View File
@@ -32,8 +32,7 @@ import StringIO
import errno
import logging
import os
import signal
import platform
import socket
import sys
import threading
@@ -43,7 +42,6 @@ import werkzeug.serving
import werkzeug.contrib.fixers
import openerp
import openerp.modules
import openerp.tools.config as config
import websrv_lib
@@ -426,7 +424,11 @@ def serve(interface, port, threaded):
"""
global httpd
httpd = werkzeug.serving.make_server(interface, port, application, threaded=threaded)
if not openerp.evented:
httpd = werkzeug.serving.make_server(interface, port, application, threaded=threaded)
else:
from gevent.wsgi import WSGIServer
httpd = WSGIServer((interface, port), application)
httpd.serve_forever()
def start_service():
@@ -446,8 +448,13 @@ def stop_service():
The server is supposed to have been started by start_server() above.
"""
if httpd:
httpd.shutdown()
close_socket(httpd.socket)
if not openerp.evented:
httpd.shutdown()
close_socket(httpd.socket)
else:
import gevent
httpd.stop()
gevent.shutdown()
def close_socket(sock):
""" Closes a socket instance cleanly
+32 -1
View File
@@ -20,7 +20,8 @@ class m(openerp.osv.osv.Model):
raise openerp.exceptions.Warning('description')
def generate_redirect_warning(self, cr, uid, ids, context=None):
raise openerp.exceptions.RedirectWarning('description', self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'menu_action_res_users'), 'go to the redirection')
dummy, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'test_exceptions', 'action_test_exceptions')
raise openerp.exceptions.RedirectWarning('description', action_id, 'go to the redirection')
def generate_access_denied(self, cr, uid, ids, context=None):
raise openerp.exceptions.AccessDenied()
@@ -34,4 +35,34 @@ class m(openerp.osv.osv.Model):
def generate_undefined(self, cr, uid, ids, context=None):
self.surely_undefined_symbol
def generate_except_osv_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_except_osv, context)
def generate_except_orm_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_except_orm, context)
def generate_warning_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_warning, context)
def generate_redirect_warning_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_redirect_warning, context)
def generate_access_denied_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_access_denied, context)
def generate_access_error_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_access_error, context)
def generate_exc_access_denied_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_exc_access_denied, context)
def generate_undefined_safe_eval(self, cr, uid, ids, context=None):
self.generate_safe_eval(cr, uid, ids, self.generate_undefined, context)
def generate_safe_eval(self, cr, uid, ids, f, context):
globals_dict = { 'generate': lambda *args: f(cr, uid, ids, context) }
openerp.tools.safe_eval.safe_eval("generate()", mode='exec', globals_dict=globals_dict)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+40 -2
View File
@@ -7,9 +7,11 @@
<field name="model">test.exceptions.model</field>
<field name="arch" type="xml">
<form string="Test exceptions">
<label string="Each button generates a specific exception on the server. The text on the right is the expected representation of the exception when displayed on the client."/>
<label string="Each button generates a specific exception on the server. The text on the right is the expected representation of the exception when displayed on the client. Button marked with a '*' use safe_eval()."/>
<separator string="" colspan="8"/>
<group colspan="8" col="8">
<group colspan="4" col="8">
<group colspan="8" col="8">
<separator string="" colspan="8"/>
<button name="generate_except_osv" string="except_osv" type="object" icon="gtk-ok" colspan="1"/>
<label string="Warning-description"/>
</group>
@@ -41,6 +43,42 @@
<button name="generate_undefined" string="Undefined" type="object" icon="gtk-ok" colspan="1"/>
<label string="Server error-traceback"/>
</group>
</group>
<group colspan="4" col="8">
<group colspan="8" col="8">
<button name="generate_except_osv_safe_eval" string="except_osv*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Warning-description"/>
</group>
<group colspan="8" col="8">
<button name="generate_except_orm_safe_eval" string="except_orm*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Warning-description"/>
</group>
<group colspan="8" col="8">
<button name="generate_warning_safe_eval" string="Warning*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Warning-description"/>
</group>
<group colspan="8" col="8">
<button name="generate_redirect_warning_safe_eval" string="RedirectWarning*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Warning-description-redirection button"/>
</group>
<group colspan="8" col="8">
<button name="generate_access_denied_safe_eval" string="AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Access denied-traceback"/>
</group>
<group colspan="8" col="8">
<button name="generate_access_error_safe_eval" string="AccessError*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Access rights error-description"/>
</group>
<group colspan="8" col="8">
<button name="generate_exc_access_denied_safe_eval" string="Exc AccessDenied*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Access denied-traceback"/>
</group>
<group colspan="8" col="8">
<button name="generate_undefined_safe_eval" string="Undefined*" type="object" icon="gtk-ok" colspan="1"/>
<label string="Server error-traceback"/>
</group>
</group>
</group>
</form>
</field>
</record>

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