[MERGE] forward port of branch saas-3 up to revid 3995 chs@openerp.com-20140407144625-jfimvsx4mgrkztqv
bzr revid: chs@openerp.com-20140407161728-ccudwnqo970ac8ay
This commit is contained in:
commit
f1d3f2a0c4
|
@ -151,8 +151,15 @@ def ensure_db(redirect='/web/database/selector'):
|
|||
# may depend on data injected by the database route dispatcher.
|
||||
# Thus, we redirect the user to the same page but with the session cookie set.
|
||||
# This will force using the database route dispatcher...
|
||||
r = request.httprequest
|
||||
url_redirect = r.base_url
|
||||
if r.query_string:
|
||||
# Can't use werkzeug.wrappers.BaseRequest.url with encoded hashes:
|
||||
# https://github.com/amigrave/werkzeug/commit/b4a62433f2f7678c234cdcac6247a869f90a7eb7
|
||||
url_redirect += '?' + r.query_string
|
||||
response = werkzeug.utils.redirect(url_redirect, 302)
|
||||
request.session.db = db
|
||||
abort_and_redirect(request.httprequest.url)
|
||||
abort_and_redirect(url_redirect)
|
||||
|
||||
# if db not provided, use the session one
|
||||
if not db:
|
||||
|
@ -649,6 +656,9 @@ class Home(http.Controller):
|
|||
ensure_db()
|
||||
|
||||
if request.session.uid:
|
||||
if kw.get('redirect'):
|
||||
return werkzeug.utils.redirect(kw.get('redirect'), 303)
|
||||
|
||||
headers = {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
|
@ -661,6 +671,9 @@ class Home(http.Controller):
|
|||
def web_login(self, redirect=None, **kw):
|
||||
ensure_db()
|
||||
|
||||
if request.httprequest.method == 'GET' and redirect and request.session.uid:
|
||||
return http.redirect_with_hash(redirect)
|
||||
|
||||
if not request.uid:
|
||||
request.uid = openerp.SUPERUSER_ID
|
||||
|
||||
|
@ -1202,11 +1215,14 @@ class DataSet(http.Controller):
|
|||
|
||||
def _call_kw(self, model, method, args, kwargs):
|
||||
# Temporary implements future display_name special field for model#read()
|
||||
if method == 'read' and kwargs.get('context', {}).get('future_display_name'):
|
||||
if method in ('read', 'search_read') and kwargs.get('context', {}).get('future_display_name'):
|
||||
if 'display_name' in args[1]:
|
||||
names = dict(request.session.model(model).name_get(args[0], **kwargs))
|
||||
if method == 'read':
|
||||
names = dict(request.session.model(model).name_get(args[0], **kwargs))
|
||||
else:
|
||||
names = dict(request.session.model(model).name_search('', args[0], **kwargs))
|
||||
args[1].remove('display_name')
|
||||
records = request.session.model(model).read(*args, **kwargs)
|
||||
records = getattr(request.session.model(model), method)(*args, **kwargs)
|
||||
for record in records:
|
||||
record['display_name'] = \
|
||||
names.get(record['id']) or "%s#%d" % (model, (record['id']))
|
||||
|
|
|
@ -1284,12 +1284,14 @@
|
|||
width: 100%;
|
||||
}
|
||||
.openerp .oe_view_manager .oe_view_manager_body {
|
||||
display: table-row;
|
||||
height: inherit;
|
||||
}
|
||||
.openerp .oe_view_manager .oe_view_manager_view_kanban:not(:empty) {
|
||||
height: inherit;
|
||||
}
|
||||
.openerp .oe_view_manager[data-view-type=kanban] .oe_view_manager_body {
|
||||
display: table-row;
|
||||
}
|
||||
.openerp .oe_view_manager table.oe_view_manager_header {
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
|
@ -2398,6 +2400,7 @@
|
|||
}
|
||||
.openerp .oe_hidden_input_file {
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.openerp .oe_hidden_input_file input.oe_form_binary_file {
|
||||
z-index: 0;
|
||||
|
|
|
@ -1068,10 +1068,12 @@ $sheet-padding: 16px
|
|||
height: inherit
|
||||
width: 100%
|
||||
.oe_view_manager_body
|
||||
display: table-row
|
||||
height: inherit
|
||||
.oe_view_manager_view_kanban:not(:empty)
|
||||
height: inherit
|
||||
&[data-view-type=kanban]
|
||||
.oe_view_manager_body
|
||||
display: table-row
|
||||
|
||||
table.oe_view_manager_header
|
||||
border-collapse: separate
|
||||
|
@ -1951,6 +1953,7 @@ $sheet-padding: 16px
|
|||
// Position: relative is used for the hidden input[type=file]
|
||||
// Do not remove it anymore !
|
||||
position: relative
|
||||
overflow-x: hidden
|
||||
input.oe_form_binary_file
|
||||
z-index: 0
|
||||
line-height: 0
|
||||
|
|
|
@ -105,9 +105,6 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
autoOpen: false,
|
||||
position: [false, 40],
|
||||
buttons: null,
|
||||
beforeClose: function () {
|
||||
self.trigger("closing");
|
||||
},
|
||||
resizeStop: function() {
|
||||
self.trigger("resized");
|
||||
},
|
||||
|
@ -208,8 +205,9 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
/**
|
||||
Closes the popup, if destroy_on_close was passed to the constructor, it is also destroyed.
|
||||
*/
|
||||
close: function() {
|
||||
close: function(reason) {
|
||||
if (this.dialog_inited && this.$el.is(":data(dialog)")) {
|
||||
this.trigger("closing", reason);
|
||||
this.$el.dialog('close');
|
||||
}
|
||||
},
|
||||
|
@ -225,14 +223,14 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
/**
|
||||
Destroys the popup, also closes it.
|
||||
*/
|
||||
destroy: function () {
|
||||
destroy: function (reason) {
|
||||
this.$buttons.remove();
|
||||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
if (! this.__tmp_dialog_closing) {
|
||||
this.__tmp_dialog_destroying = true;
|
||||
this.close();
|
||||
this.close(reason);
|
||||
this.__tmp_dialog_destroying = undefined;
|
||||
}
|
||||
if (this.dialog_inited && !this.isDestroyed() && this.$el.is(":data(dialog)")) {
|
||||
|
@ -256,7 +254,7 @@ instance.web.CrashManager = instance.web.Class.extend({
|
|||
new (handler)(this, error).display();
|
||||
return;
|
||||
}
|
||||
if (error.data.name === "openerp.http.SessionExpiredException") {
|
||||
if (error.data.name === "openerp.http.SessionExpiredException" || error.data.name === "werkzeug.exceptions.Forbidden") {
|
||||
this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }});
|
||||
return;
|
||||
}
|
||||
|
@ -1264,9 +1262,9 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
};
|
||||
self.action_manager.do_action(result);
|
||||
var form = self.action_manager.dialog_widget.views.form.controller;
|
||||
form.on("on_button_cancel", self.action_manager.dialog, self.action_manager.dialog.close);
|
||||
form.on("on_button_cancel", self.action_manager, self.action_manager.dialog_stop);
|
||||
form.on('record_saved', self, function() {
|
||||
self.action_manager.dialog.close();
|
||||
self.action_manager.dialog_stop();
|
||||
self.update_logo();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -271,7 +271,12 @@ instance.web.Session.include( /** @lends instance.web.Session# */{
|
|||
for(var i=0; i<cookies.length; ++i) {
|
||||
var cookie = cookies[i].replace(/^\s*/, '');
|
||||
if(cookie.indexOf(nameEQ) === 0) {
|
||||
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
|
||||
try {
|
||||
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
|
||||
} catch(err) {
|
||||
// wrong cookie, delete it
|
||||
this.set_cookie(name, '', -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -457,9 +457,17 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
|
|||
return $.Deferred().resolve([]);
|
||||
|
||||
options = options || {};
|
||||
return this._model.call('read',
|
||||
[ids, fields || false],
|
||||
{context: this.get_context(options.context)})
|
||||
var method = 'read';
|
||||
var ids_arg = ids;
|
||||
var context = this.get_context(options.context);
|
||||
if (options.check_access_rule === true){
|
||||
method = 'search_read';
|
||||
ids_arg = [['id', 'in', ids]];
|
||||
context = new instance.web.CompoundContext(context, {active_test: false});
|
||||
}
|
||||
return this._model.call(method,
|
||||
[ids_arg, fields || false],
|
||||
{context: context})
|
||||
.then(function (records) {
|
||||
if (records.length <= 1) { return records; }
|
||||
var indexes = {};
|
||||
|
|
|
@ -1560,9 +1560,6 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
|
|||
this.model = new instance.web.Model(this.attrs.relation);
|
||||
},
|
||||
complete: function (needle) {
|
||||
if (this.attrs.operator || this.attrs.filter_domain) {
|
||||
return this._super(needle);
|
||||
}
|
||||
var self = this;
|
||||
// FIXME: "concurrent" searches (multiple requests, mis-ordered responses)
|
||||
var context = instance.web.pyeval.eval(
|
||||
|
|
|
@ -967,9 +967,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
context: {
|
||||
'bin_size': true,
|
||||
'future_display_name': true
|
||||
}
|
||||
},
|
||||
check_access_rule: true
|
||||
}).then(function(r) {
|
||||
self.trigger('load_record', r);
|
||||
}).fail(function (){
|
||||
self.do_action('history_back');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1990,8 +1993,10 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
|
|||
var context = this.build_context();
|
||||
return this.view.do_execute_action(
|
||||
_.extend({}, this.node.attrs, {context: context}),
|
||||
this.view.dataset, this.view.datarecord.id, function () {
|
||||
self.view.recursive_reload();
|
||||
this.view.dataset, this.view.datarecord.id, function (reason) {
|
||||
if (!_.isObject(reason)) {
|
||||
self.view.recursive_reload();
|
||||
}
|
||||
});
|
||||
},
|
||||
check_disable: function() {
|
||||
|
@ -2264,7 +2269,7 @@ instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.Reini
|
|||
/**
|
||||
Some hack to make placeholders work in ie9.
|
||||
*/
|
||||
if ($.browser.msie && $.browser.version === "9.0") {
|
||||
if (!('placeholder' in document.createElement('input'))) {
|
||||
document.addEventListener("DOMNodeInserted",function(event){
|
||||
var nodename = event.target.nodeName.toLowerCase();
|
||||
if ( nodename === "input" || nodename == "textarea" ) {
|
||||
|
|
|
@ -537,7 +537,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
[record.get('id')],
|
||||
_.pluck(_(this.columns).filter(function (r) {
|
||||
return r.tag === 'field';
|
||||
}), 'name')
|
||||
}), 'name'),
|
||||
{check_access_rule: true}
|
||||
).done(function (records) {
|
||||
var values = records[0];
|
||||
if (!values) {
|
||||
|
|
|
@ -25,9 +25,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
this._super.apply(this, arguments);
|
||||
this.$el.on('click', 'a.oe_breadcrumb_item', this.on_breadcrumb_clicked);
|
||||
},
|
||||
dialog_stop: function () {
|
||||
dialog_stop: function (reason) {
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog.destroy(reason);
|
||||
}
|
||||
this.dialog = null;
|
||||
},
|
||||
|
@ -408,7 +408,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
|
||||
this.dialog_widget.destroy();
|
||||
}
|
||||
this.dialog_stop();
|
||||
this.dialog_stop(executor.action);
|
||||
this.dialog = new instance.web.Dialog(this, {
|
||||
dialogClass: executor.klass,
|
||||
});
|
||||
|
@ -426,7 +426,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
this.dialog.open();
|
||||
return initialized;
|
||||
} else {
|
||||
this.dialog_stop();
|
||||
this.dialog_stop(executor.action);
|
||||
this.inner_action = executor.action;
|
||||
this.inner_widget = widget;
|
||||
executor.post_process(widget);
|
||||
|
@ -639,7 +639,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
|||
this.$el
|
||||
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
|
||||
.parent().addClass('active');
|
||||
|
||||
this.$el.attr("data-view-type", view_type);
|
||||
return $.when(view_promise).done(function () {
|
||||
_.each(_.keys(self.views), function(view_name) {
|
||||
var controller = self.views[view_name].controller;
|
||||
|
@ -1402,7 +1402,7 @@ instance.web.View = instance.web.Widget.extend({
|
|||
// Wrong group_by values will simply fail and forbid rendering of the destination view
|
||||
var ncontext = new instance.web.CompoundContext(
|
||||
_.object(_.reject(_.pairs(dataset.get_context().eval()), function(pair) {
|
||||
return pair[0].match('^(?:(?:default_|search_default_).+|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
|
||||
return pair[0].match('^(?:(?:default_|search_default_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
|
||||
}))
|
||||
);
|
||||
ncontext.add(action_data.context || {});
|
||||
|
|
|
@ -1264,7 +1264,6 @@
|
|||
<td>
|
||||
<t t-call="HiddenInputFile">
|
||||
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
|
||||
<t t-set="fileupload_style" t-translation="off">width: 83px;</t>
|
||||
<button class="oe_button oe_field_button" type="button">
|
||||
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
|
||||
<span>Select</span>
|
||||
|
|
|
@ -81,7 +81,7 @@ openerp.testing.section('editor', {
|
|||
test('toggle-edition-save', {
|
||||
asserts: 4,
|
||||
setup: function (instance, $s, mock) {
|
||||
mock('test.model:read', function () {
|
||||
mock('test.model:search_read', function () {
|
||||
return [{id: 42, a: false, b: false, c: false}];
|
||||
});
|
||||
}
|
||||
|
@ -183,6 +183,15 @@ openerp.testing.section('list.edition', {
|
|||
}
|
||||
return [];
|
||||
});
|
||||
mock('demo:search_read', function (args) {
|
||||
// args[0][0] = ["id", "=", 42]
|
||||
// args[0][0] = 42
|
||||
var id = args[0][0][2];
|
||||
if (id in records) {
|
||||
return [records[id]];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
mock('demo:fields_view_get', function () {
|
||||
return {
|
||||
type: 'tree',
|
||||
|
@ -316,11 +325,13 @@ openerp.testing.section('list.edition.onwrite', {
|
|||
mock('demo:read', function (args, kwargs) {
|
||||
if (_.isEmpty(args[0])) {
|
||||
return [];
|
||||
} else if (_.isEqual(args[0], [1])) {
|
||||
return [
|
||||
{id: 1, a: 'some value'}
|
||||
];
|
||||
} else if (_.isEqual(args[0], [42])) {
|
||||
}
|
||||
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||
});
|
||||
mock('demo:search_read', function (args, kwargs) {
|
||||
if (_.isEqual(args[0], [['id', 'in', [1]]])) {
|
||||
return [{id: 1, a: 'some value'}];
|
||||
} else if (_.isEqual(args[0], [['id', 'in', [42]]])) {
|
||||
return [ {id: 42, a: 'foo'} ];
|
||||
}
|
||||
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||
|
|
|
@ -18,8 +18,12 @@ openerp.testing.section('list.buttons', {
|
|||
return [
|
||||
{id: 1, a: 'foo'}, {id: 2, a: 'bar'}, {id: 3, a: 'baz'}
|
||||
];
|
||||
} else if (_.isEqual(args[0], [2])) {
|
||||
// button action virtually removed record
|
||||
}
|
||||
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||
});
|
||||
mock('demo:search_read', function (args, kwargs) {
|
||||
console.log(args);
|
||||
if (_.isEqual(args[0], [['id', 'in', [2]]])) {
|
||||
return [];
|
||||
}
|
||||
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||
|
|
|
@ -620,29 +620,33 @@ openerp.testing.section('search.completions', {
|
|||
{relation: 'dummy.model'}, view);
|
||||
return f.complete("bob");
|
||||
});
|
||||
test("M2O custom operator", {asserts: 6}, function (instance) {
|
||||
var view = { inputs: [], };
|
||||
test("M2O custom operator", {asserts: 10}, function (instance, $s, mock) {
|
||||
mock('dummy.model:name_search', function (args, kwargs) {
|
||||
deepEqual(args, [], "should have no positional arguments");
|
||||
// the operator is meant for the final search term generation, not the autocompletion
|
||||
equal(kwargs.operator, undefined, "operator should not be used for autocompletion")
|
||||
strictEqual(kwargs.name, 'bob');
|
||||
return [[42, "Match"]];
|
||||
});
|
||||
var view = {inputs: [], dataset: {get_context: function () {}}};
|
||||
var f = new instance.web.search.ManyToOneField(
|
||||
{attrs: {string: 'Dummy', operator:'ilike'}},
|
||||
{attrs: {string: 'Dummy', operator: 'ilike'}},
|
||||
{relation: 'dummy.model'}, view);
|
||||
|
||||
return f.complete('bob')
|
||||
.done(function (completions) {
|
||||
equal(completions.length, 1, "should provide a single completion");
|
||||
var c = completions[0];
|
||||
equal(c.label, "Search <em>Dummy</em> for: <strong>bob</strong>",
|
||||
"should propose fuzzy searching of the value");
|
||||
ok(c.facet, "should have a facet");
|
||||
.done(function (c) {
|
||||
equal(c.length, 2, "should return result + title");
|
||||
var title = c[0];
|
||||
equal(title.label, f.attrs.string, "title should match field name");
|
||||
ok(!title.facet, "title should not have a facet");
|
||||
|
||||
var facet = new instance.web.search.Facet(c.facet);
|
||||
equal(facet.get('category'), f.attrs.string,
|
||||
"completion facet should bear the field's name");
|
||||
strictEqual(facet.get('field'), f,
|
||||
"completion facet should yield the field");
|
||||
deepEqual(facet.values.toJSON(), [{label: 'bob', value: 'bob'}],
|
||||
"facet should have a single value using the completion item");
|
||||
var f1 = new instance.web.search.Facet(c[1].facet);
|
||||
equal(c[1].label, "Match");
|
||||
equal(f1.get('category'), f.attrs.string);
|
||||
equal(f1.get('field'), f);
|
||||
deepEqual(f1.values.toJSON(), [{label: 'Match', value: 42}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('Integer: invalid', {asserts: 1}, function (instance) {
|
||||
var view = {inputs: []};
|
||||
var f = new instance.web.search.IntegerField(
|
||||
|
|
|
@ -6,6 +6,7 @@ OpenERP Web Calendar view.
|
|||
==========================
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA, Valentino Lab (Kalysto)',
|
||||
'version': '2.0',
|
||||
'depends': ['web'],
|
||||
'data' : [],
|
||||
|
|
|
@ -217,6 +217,7 @@ openerp.web_calendar = function(instance) {
|
|||
.then(function (create_right) {
|
||||
self.create_right = create_right;
|
||||
self.init_calendar().then(function() {
|
||||
$(window).trigger('resize');
|
||||
self.trigger('calendar_view_loaded', fv);
|
||||
self.ready.resolve();
|
||||
});
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
This is a hack for IE9 ...
|
||||
Attrs : width and height are not take in consideration
|
||||
*/
|
||||
.openerp svg:not(:root) {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.graph_pivot_mode {
|
||||
position:initial;
|
||||
}
|
||||
|
|
|
@ -582,7 +582,11 @@
|
|||
var tag;
|
||||
//if ('\v' == 'v') /* ie only */ {
|
||||
if (document.createStyleSheet) {
|
||||
document.createStyleSheet().cssText = css;
|
||||
try {
|
||||
document.createStyleSheet().cssText = css;
|
||||
} catch(err) {
|
||||
console.log('Failed - IE9 limit 31 sheets CSS');
|
||||
}
|
||||
} else {
|
||||
tag = document.createElement('style');
|
||||
tag.type = 'text/css';
|
||||
|
|
Loading…
Reference in New Issue