[MERGE] forward port branch saas-14 up to 8b8f04e14c
This commit is contained in:
@@ -260,11 +260,13 @@ class account_payment(models.Model):
|
||||
self.partner_type = 'customer'
|
||||
elif self.payment_type == 'outbound':
|
||||
self.partner_type = 'supplier'
|
||||
else:
|
||||
self.partner_type = False
|
||||
# Set payment method domain
|
||||
res = self._onchange_journal()
|
||||
if not res.get('domain', {}):
|
||||
res['domain'] = {}
|
||||
res['domain']['journal_id'] = self.payment_type == 'inbound' and [('at_least_one_inbound', '=', True)] or [('at_least_one_outbound', '=', True)]
|
||||
res['domain']['journal_id'] = self.payment_type == 'inbound' and [('at_least_one_inbound', '=', True)] or self.payment_type == 'outbound' and [('at_least_one_outbound', '=', True)] or []
|
||||
res['domain']['journal_id'].append(('type', 'in', ('bank', 'cash')))
|
||||
return res
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ class PadCommon(models.AbstractModel):
|
||||
myPad.setText(path, (html2plaintext(vals[field.pad_content_field]).encode('utf-8')))
|
||||
# Update the `pad_content_field` if the pad is modified
|
||||
for k, v in vals.items():
|
||||
field = self._fields[k]
|
||||
field = self._fields.get(k)
|
||||
if hasattr(field, 'pad_content_field'):
|
||||
vals[field.pad_content_field] = self.pad_get_content(v)
|
||||
|
||||
|
||||
@@ -87,13 +87,11 @@ class AccountInvoice(models.Model):
|
||||
self.purchase_id = False
|
||||
return {}
|
||||
|
||||
@api.onchange('currency_id', 'date_invoice')
|
||||
@api.onchange('currency_id')
|
||||
def _onchange_currency_id(self):
|
||||
if self.currency_id:
|
||||
for line in self.invoice_line_ids.filtered(lambda r: r.purchase_line_id):
|
||||
if line.purchase_id.currency_id != self.currency_id:
|
||||
line.price_unit = line.purchase_id.currency_id.with_context(date=self.date_invoice)\
|
||||
.compute(line.purchase_line_id.price_unit, self.currency_id, round=False)
|
||||
line.price_unit = line.purchase_id.currency_id.with_context(date=self.date_invoice).compute(line.purchase_line_id.price_unit, self.currency_id, round=False)
|
||||
|
||||
@api.onchange('invoice_line_ids')
|
||||
def _onchange_origin(self):
|
||||
|
||||
@@ -32,14 +32,13 @@ data.dom_ready = dom_ready;
|
||||
var all_ready;
|
||||
data.ready = function () {
|
||||
if (!all_ready) {
|
||||
all_ready = $.when(dom_ready, ajax.loadXML());
|
||||
all_ready = dom_ready;
|
||||
if (!odoo._modules) {
|
||||
all_ready.then(load_translations);
|
||||
all_ready = all_ready.then(load_translations);
|
||||
}
|
||||
} else { // can add async template
|
||||
all_ready = $.when(dom_ready, ajax.loadXML());
|
||||
}
|
||||
return all_ready;
|
||||
// also load new templates since previous ready call
|
||||
return $.when(all_ready, ajax.loadXML());
|
||||
};
|
||||
|
||||
function load_translations() {
|
||||
|
||||
@@ -280,7 +280,15 @@ var RTE = Widget.extend({
|
||||
} else {
|
||||
rng = $editable.data('range') || rng;
|
||||
}
|
||||
rng.select();
|
||||
try {
|
||||
// TODO this line might break for unknown reasons. I suppose that
|
||||
// the created range is an invalid one. As it might be tricky to
|
||||
// adapt that line and that it is not a critical one, temporary fix
|
||||
// is to ignore the errors that this generates.
|
||||
rng.select();
|
||||
} catch (e) {
|
||||
console.log('error', e);
|
||||
}
|
||||
history.recordUndo($editable, event, internal_history);
|
||||
},
|
||||
/**
|
||||
@@ -567,8 +575,8 @@ var RTE = Widget.extend({
|
||||
onEnableEditableArea: function ($editable) {
|
||||
},
|
||||
|
||||
onMouseup: function (event) {
|
||||
var $target = $(event.target);
|
||||
onMouseup: function (ev) {
|
||||
var $target = $(ev.target);
|
||||
var $editable = $target.closest('.o_editable');
|
||||
|
||||
if (!$editable.length) {
|
||||
@@ -580,26 +588,16 @@ var RTE = Widget.extend({
|
||||
self.historyRecordUndo($target, 'activate', true);
|
||||
});
|
||||
|
||||
// To Fix Google Chrome Tripleclick Issue, which selects the ending
|
||||
// whitespace characters (so Tripleclicking then typing text will remove
|
||||
// the whole paragraph instead of its content).
|
||||
// http://stackoverflow.com/questions/38467334/why-does-google-chrome-always-add-space-after-selected-text
|
||||
if ($.browser.chrome === true && event.originalEvent.detail === 3) {
|
||||
var currentSelection = range.create();
|
||||
if (currentSelection.sc.parentNode === currentSelection.ec) {
|
||||
_selectSC(currentSelection);
|
||||
} else if (currentSelection.eo === 0) {
|
||||
var $hasNext = $(currentSelection.sc).parent();
|
||||
while (!$hasNext.next().length && !$hasNext.is('body')) {
|
||||
$hasNext = $hasNext.parent();
|
||||
}
|
||||
if ($hasNext.next()[0] === currentSelection.ec) {
|
||||
_selectSC(currentSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
function _selectSC(selection) {
|
||||
range.create(selection.sc, selection.so, selection.sc, selection.sc.length).select();
|
||||
// Browsers select different content from one to another after a
|
||||
// triple click (especially: if triple-clicking on a paragraph on
|
||||
// Chrome, blank characters of the element following the paragraph are
|
||||
// selected too)
|
||||
//
|
||||
// The triple click behavior is reimplemented for all browsers here
|
||||
if (ev.originalEvent.detail === 3) {
|
||||
// Select the whole content inside the deepest DOM element that was
|
||||
// triple-clicked
|
||||
range.create(ev.target, 0, ev.target, ev.target.childNodes.length).select();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -422,6 +422,9 @@ dom.isImg = function (node) {
|
||||
};
|
||||
var fn_is_forbidden_node = dom.isForbiddenNode || function () {};
|
||||
dom.isForbiddenNode = function (node) {
|
||||
if (node.tagName === "BR") {
|
||||
return false;
|
||||
}
|
||||
return fn_is_forbidden_node(node) || $(node).is(".media_iframe_video");
|
||||
};
|
||||
var fn_is_img_font = dom.isImgFont || function () {};
|
||||
|
||||
@@ -703,10 +703,16 @@ dom.isRemovableEmptyNode = function (node) {
|
||||
dom.isForbiddenNode = function (node) {
|
||||
return node.tagName === "BR" || $(node).is(".fa, img");
|
||||
};
|
||||
dom.listBetween = function (sc, ec) {
|
||||
/**
|
||||
* @todo 'so' and 'eo' were added as a bugfix and are not given everytime. They
|
||||
* however should be as the function may be wrong without them (for example,
|
||||
* when asking the list between an element and its parent, as there is no path
|
||||
* from the beginning of the former to the beginning of the later).
|
||||
*/
|
||||
dom.listBetween = function (sc, ec, so, eo) {
|
||||
var nodes = [];
|
||||
var ancestor = dom.commonAncestor(sc, ec);
|
||||
dom.walkPoint({'node': sc, 'offset': 0}, {'node': ec, 'offset': 0}, function (point) {
|
||||
dom.walkPoint({'node': sc, 'offset': so || 0}, {'node': ec, 'offset': eo || 0}, function (point) {
|
||||
if (ancestor !== point.node || ancestor === sc || ancestor === ec) {
|
||||
nodes.push(point.node);
|
||||
}
|
||||
@@ -1926,7 +1932,7 @@ $.summernote.pluginEvents.formatBlock = function (event, editor, layoutInfo, sTa
|
||||
}
|
||||
|
||||
// fix by odoo because if you select a style in a li with no p tag all the ul is wrapped by the style tag
|
||||
var nodes = dom.listBetween(r.sc, r.ec);
|
||||
var nodes = dom.listBetween(r.sc, r.ec, r.so, r.eo);
|
||||
for (var i=0; i<nodes.length; i++) {
|
||||
if (dom.isBR(nodes[i]) || (dom.isText(nodes[i]) && dom.isVisibleText(nodes[i])) || dom.isB(nodes[i]) || dom.isU(nodes[i]) || dom.isS(nodes[i]) || dom.isI(nodes[i]) || dom.isFont(nodes[i])) {
|
||||
var ancestor = dom.ancestor(nodes[i], isFormatNode);
|
||||
|
||||
@@ -209,7 +209,7 @@ class BlogPost(models.Model):
|
||||
def write(self, vals):
|
||||
result = True
|
||||
for post in self:
|
||||
copy_vals = vals
|
||||
copy_vals = dict(vals)
|
||||
if 'website_published' in vals and 'published_date' not in vals and post.published_date <= fields.Datetime.now():
|
||||
copy_vals['published_date'] = vals['website_published'] and fields.Datetime.now() or False
|
||||
result &= super(BlogPost, self).write(copy_vals)
|
||||
|
||||
@@ -374,7 +374,7 @@ class IrActionsActWindowView(models.Model):
|
||||
_name = 'ir.actions.act_window.view'
|
||||
_table = 'ir_act_window_view'
|
||||
_rec_name = 'view_id'
|
||||
_order = 'sequence'
|
||||
_order = 'sequence,id'
|
||||
|
||||
sequence = fields.Integer()
|
||||
view_id = fields.Many2one('ir.ui.view', string='View')
|
||||
|
||||
@@ -14,7 +14,6 @@ from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationErro
|
||||
from odoo.osv import expression
|
||||
from odoo.service.db import check_super
|
||||
from odoo.tools import partition
|
||||
from odoo.http import root
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -373,8 +372,6 @@ class Users(models.Model):
|
||||
if any(key.startswith('context_') or key in ('lang', 'tz') for key in values):
|
||||
self.context_get.clear_cache(self)
|
||||
if any(key in values for key in ['active'] + USER_PRIVATE_FIELDS):
|
||||
# force deletion of all sessions for these users
|
||||
root.session_store.delete_sessions_for_uids(self.ids)
|
||||
db = self._cr.dbname
|
||||
for id in self.ids:
|
||||
self.__uid_cache[db].pop(id, None)
|
||||
@@ -388,10 +385,7 @@ class Users(models.Model):
|
||||
db = self._cr.dbname
|
||||
for id in self.ids:
|
||||
self.__uid_cache[db].pop(id, None)
|
||||
res = super(Users, self).unlink()
|
||||
# force deletion of all sessions for these users
|
||||
root.session_store.delete_sessions_for_uids(self.ids)
|
||||
return res
|
||||
return super(Users, self).unlink()
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
|
||||
+4
-12
@@ -1040,6 +1040,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
self.db = db
|
||||
self.uid = uid
|
||||
self.login = login
|
||||
self.password = password
|
||||
request.uid = uid
|
||||
request.disable_db = False
|
||||
|
||||
@@ -1054,6 +1055,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
"""
|
||||
if not self.db or not self.uid:
|
||||
raise SessionExpiredException("Session expired")
|
||||
security.check(self.db, self.uid, self.password)
|
||||
|
||||
def logout(self, keep_db=False):
|
||||
for k in self.keys():
|
||||
@@ -1066,6 +1068,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
self.setdefault("db", None)
|
||||
self.setdefault("uid", None)
|
||||
self.setdefault("login", None)
|
||||
self.setdefault("password", None)
|
||||
self.setdefault("context", {})
|
||||
|
||||
def get_context(self):
|
||||
@@ -1278,17 +1281,6 @@ class DisableCacheMiddleware(object):
|
||||
start_response(status, new_headers)
|
||||
return self.app(environ, start_wrapped)
|
||||
|
||||
class OdooSessionStore(werkzeug.contrib.sessions.FilesystemSessionStore):
|
||||
def delete_sessions_for_uids(self, uids):
|
||||
# pretty expensive on large session stores, especially non-local!
|
||||
uids = set(uids)
|
||||
_logger.info('Deleting all HTTP sessions for UIDs %s', uids)
|
||||
for sid in self.list():
|
||||
s = self.get(sid)
|
||||
if s.uid and s.uid in uids:
|
||||
_logger.debug('Deleting session %s', sid)
|
||||
self.delete(s)
|
||||
|
||||
class Root(object):
|
||||
"""Root WSGI application for the OpenERP Web Client.
|
||||
"""
|
||||
@@ -1300,7 +1292,7 @@ class Root(object):
|
||||
# Setup http sessions
|
||||
path = odoo.tools.config.session_dir
|
||||
_logger.debug('HTTP sessions stored in: %s', path)
|
||||
return OdooSessionStore(path, session_class=OpenERPSession)
|
||||
return werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
|
||||
|
||||
@lazy_property
|
||||
def nodb_routing_map(self):
|
||||
|
||||
Reference in New Issue
Block a user