[MERGE] forward port of branch saas-5 up to 4cb5381

Conflicts:
	openerp/addons/base/module/wizard/base_module_upgrade_view.xml
This commit is contained in:
Christophe Simonis 2014-06-22 15:36:10 +02:00
commit e862ff7033
14 changed files with 146 additions and 48 deletions

View File

@ -1,2 +0,0 @@
.*
**/node_modules

View File

@ -128,13 +128,12 @@ class sale_order(osv.osv):
sale_clause = '' sale_clause = ''
no_invoiced = False no_invoiced = False
for arg in args: for arg in args:
if arg[1] == '=': if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
if arg[2]: clause += 'AND inv.state = \'paid\''
clause += 'AND inv.state = \'paid\'' else:
else: clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
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 '
sale_clause = ', sale_order AS sale ' no_invoiced = True
no_invoiced = True
cursor.execute('SELECT rel.order_id ' \ cursor.execute('SELECT rel.order_id ' \
'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \ 'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \

View File

@ -2647,11 +2647,11 @@
.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell { .openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
padding: 4px 6px 3px; 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; color: transparent;
text-shadow: none; 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; visibility: hidden;
} }
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button { .openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button {
@ -2667,6 +2667,13 @@
min-width: 0; min-width: 0;
max-width: none; 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 { .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; height: 27px;
-moz-border-radius: 0; -moz-border-radius: 0;
@ -2678,9 +2685,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 { .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; 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; 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; 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 { .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; width: 100% !important;

View File

@ -2138,7 +2138,7 @@ $sheet-padding: 16px
.oe_list_editable .oe_list_content td.oe_list_field_cell .oe_list_editable .oe_list_content td.oe_list_field_cell
padding: 4px 6px 3px padding: 4px 6px 3px
.oe_list.oe_list_editable.oe_editing .oe_list.oe_list_editable.oe_editing
.oe_edition .oe_list_field_cell:not(.oe_readonly) .oe_edition .oe_list_field_cell
* *
visibility: hidden visibility: hidden
color: transparent color: transparent
@ -2150,6 +2150,11 @@ $sheet-padding: 16px
.oe_input_icon .oe_input_icon
margin-top: 5px margin-top: 5px
.oe_form_field .oe_form_field
&.oe_list_field_handle
color: transparent
&.oe_readonly
padding: 4px 6px 3px
text-align: left
min-width: 0 min-width: 0
max-width: none max-width: none
input, textarea input, textarea
@ -2160,9 +2165,13 @@ $sheet-padding: 16px
input, textarea, select input, textarea, select
min-width: 0 min-width: 0
&.oe_form_field_float,&.oe_form_view_integer &.oe_form_field_float,&.oe_form_view_integer
input &.oe_readonly
padding: 6px 0px 0px
text-align: right text-align: right
max-width: 100px
input
width: 100% !important width: 100% !important
text-align: right
&.oe_form_field_datetime,&.oe_form_field_date &.oe_form_field_datetime,&.oe_form_field_date
input.oe_datepicker_master input.oe_datepicker_master
width: 100% !important width: 100% !important

View File

@ -2810,7 +2810,9 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
}, },
set_dimensions: function (height, width) { set_dimensions: function (height, width) {
this._super(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.floating = false;
this.current_display = null; this.current_display = null;
this.is_started = false; this.is_started = false;
this.ignore_focusout = false;
}, },
reinit_value: function(val) { reinit_value: function(val) {
this.internal_set_value(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_delay = 200;
var ed_duration = 15000; var ed_duration = 15000;
var anyoneLoosesFocus = function (e) { var anyoneLoosesFocus = function (e) {
if (self.ignore_focusout) { return; }
var used = false; var used = false;
if (self.floating) { if (self.floating) {
if (self.last_search.length > 0) { if (self.last_search.length > 0) {
@ -3844,11 +3848,17 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
_search_create_popup: function() { _search_create_popup: function() {
this.no_ed = true; this.no_ed = true;
this.ed_def.reject(); 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) { set_dimensions: function (height, width) {
this._super(height, width); this._super(height, width);
this.$input.css('height', height); if (!this.get("effective_readonly") && this.$input)
this.$input.css('height', height);
} }
}); });

View File

@ -285,9 +285,7 @@
if (!this.editor.is_editing()) { return; } if (!this.editor.is_editing()) { return; }
for(var i=0, len=this.fields_for_resize.length; i<len; ++i) { for(var i=0, len=this.fields_for_resize.length; i<len; ++i) {
var item = this.fields_for_resize[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', at: 'left top',
of: $cell 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} * @return {jQuery.Deferred}
@ -451,13 +454,7 @@
setup_events: function () { setup_events: function () {
var self = this; var self = this;
_.each(this.editor.form.fields, function(field, field_name) { _.each(this.editor.form.fields, function(field, field_name) {
var set_invisible = function() { field.on("change:effective_readonly", self, 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; }
var item = _(self.fields_for_resize).find(function (item) { var item = _(self.fields_for_resize).find(function (item) {
return item.field === field; return item.field === field;
}); });

View File

@ -76,17 +76,23 @@ class ir_http(osv.AbstractModel):
request.uid = request.session.uid request.uid = request.session.uid
def _authenticate(self, auth_method='user'): def _authenticate(self, auth_method='user'):
if request.session.uid: try:
try: if request.session.uid:
request.session.check_security() try:
# what if error in security.check() request.session.check_security()
# -> res_users.check() # what if error in security.check()
# -> res_users.check_credentials() # -> res_users.check()
except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException): # -> res_users.check_credentials()
# All other exceptions mean undetermined status (e.g. connection pool full), except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
# let them bubble up # All other exceptions mean undetermined status (e.g. connection pool full),
request.session.logout() # let them bubble up
getattr(self, "_auth_method_%s" % auth_method)() 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 return auth_method
def _handle_exception(self, exception): def _handle_exception(self, exception):

View File

@ -496,6 +496,7 @@ class module(osv.osv):
'params': {'menu_id': menu_ids and menu_ids[0] or False} '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): def button_immediate_uninstall(self, cr, uid, ids, context=None):
""" """
Uninstall the selected module(s) immediately and fully, Uninstall the selected module(s) immediately and fully,

View File

@ -82,8 +82,7 @@
<div> <div>
<button name="button_immediate_install" states="uninstalled" string="Install" type="object" class="oe_highlight"/> <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_upgrade" states="installed" string="Upgrade" type="object" class="oe_highlight"/>
<button name="button_immediate_uninstall" states="installed" string="Uninstall" type="object" <button name="button_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_cancel" states="to remove" string="Cancel 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_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object"/>
<button name="button_install_cancel" states="to install" string="Cancel Install" type="object"/> <button name="button_install_cancel" states="to install" string="Cancel Install" type="object"/>

View File

@ -68,6 +68,20 @@ class base_module_upgrade(osv.osv_memory):
res = mod_obj.read(cr, uid, ids, ['name','state'], context) res = mod_obj.read(cr, uid, ids, ['name','state'], context)
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))} 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): def upgrade_module(self, cr, uid, ids, context=None):
ir_module = self.pool.get('ir.module.module') ir_module = self.pool.get('ir.module.module')

View File

@ -7,14 +7,15 @@
<field name="model">base.module.upgrade</field> <field name="model">base.module.upgrade</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="System Update"> <form string="System Update">
<div><label string="Your system will be updated."/></div> <p>This module will trigger the uninstallation of below modules.</p>
<div><label string="Note that this operation might take a few minutes."/></div> <p><strong>This operation will permanently erase all data currently stored by the modules!</strong></p>
<separator string="Modules to Update"/> <p>If you wish to cancel the process, press the cancel button below</p>
<separator string="Impacted Modules"/>
<field name="module_info"/> <field name="module_info"/>
<footer> <footer>
<button name="upgrade_module" string="Update" type="object" class="oe_highlight"/> <button name="upgrade_module" string="Update" type="object" class="oe_highlight"/>
or or
<button string="Cancel" class="oe_link" special="cancel"/> <button string="Cancel" class="oe_link" name="upgrade_module_cancel" type="object"/>
</footer> </footer>
</form> </form>
</field> </field>

View File

@ -28,6 +28,7 @@ from openerp.osv import osv, fields
from openerp.tools import ustr from openerp.tools import ustr
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp import exceptions from openerp import exceptions
from lxml import etree
_logger = logging.getLogger(__name__) _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): def copy(self, cr, uid, id, values, context=None):
raise osv.except_osv(_("Cannot duplicate configuration!"), "") 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): def _get_classified_fields(self, cr, uid, context=None):
""" return a dictionary with the fields classified by category:: """ return a dictionary with the fields classified by category::

View File

@ -270,8 +270,6 @@ class WebRequest(object):
to abitrary responses. Anything returned (except None) will to abitrary responses. Anything returned (except None) will
be used as response.""" be used as response."""
self._failed = exception # prevent tx commit self._failed = exception # prevent tx commit
if isinstance(exception, werkzeug.exceptions.HTTPException):
return exception
raise raise
def _call_function(self, *args, **kwargs): def _call_function(self, *args, **kwargs):
@ -538,6 +536,15 @@ class HttpRequest(WebRequest):
params.pop('session_id', None) params.pop('session_id', None)
self.params = params 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): def dispatch(self):
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'): if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
headers = { headers = {

View File

@ -46,8 +46,15 @@ SLEEP_INTERVAL = 60 # 1 min
#---------------------------------------------------------- #----------------------------------------------------------
# Werkzeug WSGI servers patched # 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 """ werkzeug Base WSGI Server patched to skip socket binding. PreforkServer
use this class, sets the socket and calls the process_request() manually 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 # should also work with systemd socket activation. This is currently untested
# and not yet used. # 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 """ werkzeug Threaded WSGI Server patched to allow reusing a listen socket
given by the environement, this is used by autoreload to keep the listen given by the environement, this is used by autoreload to keep the listen
socket open when a reload happens. socket open when a reload happens.