summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2014-06-22 15:33:22 +0200
committerChristophe Simonis <chs@odoo.com>2014-06-22 15:33:22 +0200
commit4cb5381deecdf28d2a0f6b1e7f4b16a611ec9de9 (patch)
tree6113f40cf7639b43dbe83e61416ac598c2289c72
parentc9f6587dbca08eca029d44bacc8fe587c6460028 (diff)
parent1f20f61ab4f6acf79f4db6ba9a17c1282014ef5f (diff)
[MERGE] forward port of branch saas-4 up to 1f20f61
-rw-r--r--addons/.bzrignore2
-rw-r--r--addons/sale/sale.py13
-rw-r--r--addons/web/static/src/css/base.css18
-rw-r--r--addons/web/static/src/css/base.sass13
-rw-r--r--addons/web/static/src/js/view_form.js16
-rw-r--r--addons/web/static/src/js/view_list_editable.js17
-rw-r--r--openerp/addons/base/ir/ir_http.py28
-rw-r--r--openerp/addons/base/module/module.py1
-rw-r--r--openerp/addons/base/module/module_view.xml3
-rw-r--r--openerp/addons/base/module/wizard/base_module_upgrade.py14
-rw-r--r--openerp/addons/base/module/wizard/base_module_upgrade_view.xml9
-rw-r--r--openerp/addons/base/res/res_config.py38
-rw-r--r--openerp/http.py11
-rw-r--r--openerp/service/server.py11
14 files changed, 146 insertions, 48 deletions
diff --git a/addons/.bzrignore b/addons/.bzrignore
deleted file mode 100644
index 8c348710117..00000000000
--- a/addons/.bzrignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.*
-**/node_modules
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 45c32a33b22..397bc0deccc 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -128,13 +128,12 @@ class sale_order(osv.osv):
sale_clause = ''
no_invoiced = False
for arg in args:
- if arg[1] == '=':
- if arg[2]:
- clause += 'AND inv.state = \'paid\''
- else:
- clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
- sale_clause = ', sale_order AS sale '
- no_invoiced = True
+ if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
+ clause += 'AND inv.state = \'paid\''
+ else:
+ clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
+ sale_clause = ', sale_order AS sale '
+ no_invoiced = True
cursor.execute('SELECT rel.order_id ' \
'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index b4c0d3050bf..6a82696ac4b 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -2624,11 +2624,11 @@
.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
padding: 4px 6px 3px;
}
-.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell {
color: transparent;
text-shadow: none;
}
-.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * {
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell * {
visibility: hidden;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button {
@@ -2644,6 +2644,13 @@
min-width: 0;
max-width: none;
}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_list_field_handle {
+ color: transparent;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_readonly {
+ padding: 4px 6px 3px;
+ text-align: left;
+}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
height: 27px;
-moz-border-radius: 0;
@@ -2655,9 +2662,14 @@
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field select {
min-width: 0;
}
-.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float.oe_readonly, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer.oe_readonly {
+ padding: 6px 0px 0px;
text-align: right;
+ max-width: 100px;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
width: 100% !important;
+ text-align: right;
}
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
width: 100% !important;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 136cdbdef24..69d338b637d 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -2117,7 +2117,7 @@ $sheet-padding: 16px
.oe_list_editable .oe_list_content td.oe_list_field_cell
padding: 4px 6px 3px
.oe_list.oe_list_editable.oe_editing
- .oe_edition .oe_list_field_cell:not(.oe_readonly)
+ .oe_edition .oe_list_field_cell
*
visibility: hidden
color: transparent
@@ -2129,6 +2129,11 @@ $sheet-padding: 16px
.oe_input_icon
margin-top: 5px
.oe_form_field
+ &.oe_list_field_handle
+ color: transparent
+ &.oe_readonly
+ padding: 4px 6px 3px
+ text-align: left
min-width: 0
max-width: none
input, textarea
@@ -2139,9 +2144,13 @@ $sheet-padding: 16px
input, textarea, select
min-width: 0
&.oe_form_field_float,&.oe_form_view_integer
- input
+ &.oe_readonly
+ padding: 6px 0px 0px
text-align: right
+ max-width: 100px
+ input
width: 100% !important
+ text-align: right
&.oe_form_field_datetime,&.oe_form_field_date
input.oe_datepicker_master
width: 100% !important
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index c816d186fde..1c75f341436 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -2810,7 +2810,9 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
},
set_dimensions: function (height, width) {
this._super(height, width);
- this.datewidget.$input.css('height', height);
+ if (!this.get("effective_readonly")) {
+ this.datewidget.$input.css('height', height);
+ }
}
});
@@ -3510,6 +3512,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
this.floating = false;
this.current_display = null;
this.is_started = false;
+ this.ignore_focusout = false;
},
reinit_value: function(val) {
this.internal_set_value(val);
@@ -3641,6 +3644,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
var ed_delay = 200;
var ed_duration = 15000;
var anyoneLoosesFocus = function (e) {
+ if (self.ignore_focusout) { return; }
var used = false;
if (self.floating) {
if (self.last_search.length > 0) {
@@ -3844,11 +3848,17 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
_search_create_popup: function() {
this.no_ed = true;
this.ed_def.reject();
- return instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
+ this.ignore_focusout = true;
+ this.reinit_value(false);
+ var res = instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
+ this.ignore_focusout = false;
+ this.no_ed = false;
+ return res;
},
set_dimensions: function (height, width) {
this._super(height, width);
- this.$input.css('height', height);
+ if (!this.get("effective_readonly") && this.$input)
+ this.$input.css('height', height);
}
});
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index ea2c28d9c3f..144de8eb21c 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -285,9 +285,7 @@
if (!this.editor.is_editing()) { return; }
for(var i=0, len=this.fields_for_resize.length; i<len; ++i) {
var item = this.fields_for_resize[i];
- if (!item.field.get('effective_invisible')) {
- this.resize_field(item.field, item.cell);
- }
+ this.resize_field(item.field, item.cell);
}
},
/**
@@ -306,6 +304,11 @@
at: 'left top',
of: $cell
});
+ if (field.get('effective_readonly')) {
+ field.$el.addClass('oe_readonly');
+ }
+ if(field.widget == "handle")
+ field.$el.addClass('oe_list_field_handle');
},
/**
* @return {jQuery.Deferred}
@@ -451,13 +454,7 @@
setup_events: function () {
var self = this;
_.each(this.editor.form.fields, function(field, field_name) {
- var set_invisible = function() {
- field.set({'force_invisible': field.get('effective_readonly')});
- };
- field.on("change:effective_readonly", self, set_invisible);
- set_invisible();
- field.on('change:effective_invisible', self, function () {
- if (field.get('effective_invisible')) { return; }
+ field.on("change:effective_readonly", self, function(){
var item = _(self.fields_for_resize).find(function (item) {
return item.field === field;
});
diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py
index ac3091e33b9..21c8b7ef941 100644
--- a/openerp/addons/base/ir/ir_http.py
+++ b/openerp/addons/base/ir/ir_http.py
@@ -76,17 +76,23 @@ class ir_http(osv.AbstractModel):
request.uid = request.session.uid
def _authenticate(self, auth_method='user'):
- if request.session.uid:
- try:
- request.session.check_security()
- # what if error in security.check()
- # -> res_users.check()
- # -> res_users.check_credentials()
- except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
- # All other exceptions mean undetermined status (e.g. connection pool full),
- # let them bubble up
- request.session.logout()
- getattr(self, "_auth_method_%s" % auth_method)()
+ try:
+ if request.session.uid:
+ try:
+ request.session.check_security()
+ # what if error in security.check()
+ # -> res_users.check()
+ # -> res_users.check_credentials()
+ except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
+ # All other exceptions mean undetermined status (e.g. connection pool full),
+ # let them bubble up
+ request.session.logout()
+ getattr(self, "_auth_method_%s" % auth_method)()
+ except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
+ raise
+ except Exception:
+ _logger.exception("Exception during request Authentication.")
+ raise openerp.exceptions.AccessDenied()
return auth_method
def _handle_exception(self, exception):
diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py
index eb154d4a05f..df673f95b32 100644
--- a/openerp/addons/base/module/module.py
+++ b/openerp/addons/base/module/module.py
@@ -496,6 +496,7 @@ class module(osv.osv):
'params': {'menu_id': menu_ids and menu_ids[0] or False}
}
+ #TODO remove me in master, not called anymore
def button_immediate_uninstall(self, cr, uid, ids, context=None):
"""
Uninstall the selected module(s) immediately and fully,
diff --git a/openerp/addons/base/module/module_view.xml b/openerp/addons/base/module/module_view.xml
index 110b607fd4e..9077a1f86db 100644
--- a/openerp/addons/base/module/module_view.xml
+++ b/openerp/addons/base/module/module_view.xml
@@ -82,8 +82,7 @@
<div>
<button name="button_immediate_install" states="uninstalled" string="Install" type="object" class="oe_highlight"/>
<button name="button_immediate_upgrade" states="installed" string="Upgrade" type="object" class="oe_highlight"/>
- <button name="button_immediate_uninstall" states="installed" string="Uninstall" type="object"
- confirm="Do you confirm the uninstallation of this module? This will permanently erase all data currently stored by the module!"/>
+ <button name="button_uninstall" states="installed" string="Uninstall" type="object"/>
<button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object"/>
<button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object"/>
<button name="button_install_cancel" states="to install" string="Cancel Install" type="object"/>
diff --git a/openerp/addons/base/module/wizard/base_module_upgrade.py b/openerp/addons/base/module/wizard/base_module_upgrade.py
index c75058ab835..365895be7ac 100644
--- a/openerp/addons/base/module/wizard/base_module_upgrade.py
+++ b/openerp/addons/base/module/wizard/base_module_upgrade.py
@@ -68,6 +68,20 @@ class base_module_upgrade(osv.osv_memory):
res = mod_obj.read(cr, uid, ids, ['name','state'], context)
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
+ def upgrade_module_cancel(self, cr, uid, ids, context=None):
+ mod_obj = self.pool.get('ir.module.module')
+ to_installed_ids = mod_obj.search(cr, uid, [
+ ('state', 'in', ['to upgrade', 'to remove'])])
+ if to_installed_ids:
+ mod_obj.write(cr, uid, to_installed_ids, {'state': 'installed'}, context=context)
+
+ to_uninstalled_ids = mod_obj.search(cr, uid, [
+ ('state', '=', 'to install')])
+ if to_uninstalled_ids:
+ mod_obj.write(cr, uid, to_uninstalled_ids, {'state': 'uninstalled'}, context=context)
+
+ return {'type': 'ir.actions.act_window_close'}
+
def upgrade_module(self, cr, uid, ids, context=None):
ir_module = self.pool.get('ir.module.module')
diff --git a/openerp/addons/base/module/wizard/base_module_upgrade_view.xml b/openerp/addons/base/module/wizard/base_module_upgrade_view.xml
index 1a1225befcb..10c023e5148 100644
--- a/openerp/addons/base/module/wizard/base_module_upgrade_view.xml
+++ b/openerp/addons/base/module/wizard/base_module_upgrade_view.xml
@@ -7,14 +7,15 @@
<field name="model">base.module.upgrade</field>
<field name="arch" type="xml">
<form string="System Update" version="7.0">
- <div><label string="Your system will be updated."/></div>
- <div><label string="Note that this operation might take a few minutes."/></div>
- <separator string="Modules to Update"/>
+ <p>This module will trigger the uninstallation of below modules.</p>
+ <p><strong>This operation will permanently erase all data currently stored by the modules!</strong></p>
+ <p>If you wish to cancel the process, press the cancel button below</p>
+ <separator string="Impacted Modules"/>
<field name="module_info"/>
<footer>
<button name="upgrade_module" string="Update" type="object" class="oe_highlight"/>
or
- <button string="Cancel" class="oe_link" special="cancel"/>
+ <button string="Cancel" class="oe_link" name="upgrade_module_cancel" type="object"/>
</footer>
</form>
</field>
diff --git a/openerp/addons/base/res/res_config.py b/openerp/addons/base/res/res_config.py
index 5bbd377f29d..06453526ebc 100644
--- a/openerp/addons/base/res/res_config.py
+++ b/openerp/addons/base/res/res_config.py
@@ -28,6 +28,7 @@ from openerp.osv import osv, fields
from openerp.tools import ustr
from openerp.tools.translate import _
from openerp import exceptions
+from lxml import etree
_logger = logging.getLogger(__name__)
@@ -434,6 +435,43 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
def copy(self, cr, uid, id, values, context=None):
raise osv.except_osv(_("Cannot duplicate configuration!"), "")
+ def fields_view_get(self, cr, user, view_id=None, view_type='form',
+ context=None, toolbar=False, submenu=False):
+ ret_val = super(res_config_settings, self).fields_view_get(
+ cr, user, view_id=view_id, view_type=view_type, context=context,
+ toolbar=toolbar, submenu=submenu)
+
+ doc = etree.XML(ret_val['arch'])
+
+ for field in ret_val['fields']:
+ if not field.startswith("module_"):
+ continue
+ for node in doc.xpath("//field[@name='%s']" % field):
+ if 'on_change' not in node.attrib:
+ node.set("on_change",
+ "onchange_module(%s, '%s')" % (field, field))
+
+ ret_val['arch'] = etree.tostring(doc)
+ return ret_val
+
+ def onchange_module(self, cr, uid, ids, field_value, module_name, context={}):
+ module_pool = self.pool.get('ir.module.module')
+ module_ids = module_pool.search(
+ cr, uid, [('name', '=', module_name.replace("module_", '')),
+ ('state','in', ['to install', 'installed', 'to upgrade'])],
+ context=context)
+
+ if module_ids and not field_value:
+ dep_ids = module_pool.downstream_dependencies(cr, uid, module_ids, context=context)
+ dep_name = [x.shortdesc for x in module_pool.browse(
+ cr, uid, dep_ids + module_ids, context=context)]
+ message = '\n'.join(dep_name)
+ return {'warning': {'title': _('Warning!'),
+ 'message':
+ _('Disabling this option will also uninstall the following modules \n%s' % message)
+ }}
+ return {}
+
def _get_classified_fields(self, cr, uid, context=None):
""" return a dictionary with the fields classified by category::
diff --git a/openerp/http.py b/openerp/http.py
index abee8ac2b3d..ceddf234c1c 100644
--- a/openerp/http.py
+++ b/openerp/http.py
@@ -270,8 +270,6 @@ class WebRequest(object):
to abitrary responses. Anything returned (except None) will
be used as response."""
self._failed = exception # prevent tx commit
- if isinstance(exception, werkzeug.exceptions.HTTPException):
- return exception
raise
def _call_function(self, *args, **kwargs):
@@ -538,6 +536,15 @@ class HttpRequest(WebRequest):
params.pop('session_id', None)
self.params = params
+ def _handle_exception(self, exception):
+ """Called within an except block to allow converting exceptions
+ to abitrary responses. Anything returned (except None) will
+ be used as response."""
+ try:
+ return super(HttpRequest, self)._handle_exception(exception)
+ except werkzeug.exceptions.HTTPException, e:
+ return e
+
def dispatch(self):
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
headers = {
diff --git a/openerp/service/server.py b/openerp/service/server.py
index 72e130edf8b..c0dc15caab7 100644
--- a/openerp/service/server.py
+++ b/openerp/service/server.py
@@ -46,8 +46,15 @@ SLEEP_INTERVAL = 60 # 1 min
#----------------------------------------------------------
# Werkzeug WSGI servers patched
#----------------------------------------------------------
+class LoggingBaseWSGIServerMixIn(object):
+ def handle_error(self, request, client_address):
+ t, e, _ = sys.exc_info()
+ if t == socket.error and e.errno == errno.EPIPE:
+ # broken pipe, ignore error
+ return
+ _logger.exception('Exception happened during processing of request from %s', client_address)
-class BaseWSGIServerNoBind(werkzeug.serving.BaseWSGIServer):
+class BaseWSGIServerNoBind(LoggingBaseWSGIServerMixIn, werkzeug.serving.BaseWSGIServer):
""" werkzeug Base WSGI Server patched to skip socket binding. PreforkServer
use this class, sets the socket and calls the process_request() manually
"""
@@ -74,7 +81,7 @@ class RequestHandler(werkzeug.serving.WSGIRequestHandler):
# should also work with systemd socket activation. This is currently untested
# and not yet used.
-class ThreadedWSGIServerReloadable(werkzeug.serving.ThreadedWSGIServer):
+class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving.ThreadedWSGIServer):
""" werkzeug Threaded WSGI Server patched to allow reusing a listen socket
given by the environement, this is used by autoreload to keep the listen
socket open when a reload happens.