[MERGE] jsonp branch by chs,
deferrization and cleanup of Connection, simple jsonp (not fully tested), url (push_state) is broken (+395/-262) 8 files modified bzr revid: al@openerp.com-20111216015358-e8p5bi8pl86onlbz
This commit is contained in:
commit
fc4faa599b
|
@ -6,11 +6,12 @@ import ast
|
||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
import urllib
|
||||||
import uuid
|
import uuid
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
|
|
||||||
|
@ -128,17 +129,37 @@ class JsonRequest(WebRequest):
|
||||||
"id": null}
|
"id": null}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
def dispatch(self, controller, method):
|
||||||
def dispatch(self, controller, method, requestf=None, request=None):
|
""" Calls the method asked for by the JSON-RPC2 or JSONP request
|
||||||
""" Calls the method asked for by the JSON-RPC2 request
|
|
||||||
|
|
||||||
:param controller: the instance of the controller which received the request
|
:param controller: the instance of the controller which received the request
|
||||||
:param method: the method which received the request
|
:param method: the method which received the request
|
||||||
:param requestf: a file-like object containing an encoded JSON-RPC2 request
|
|
||||||
:param request: a JSON-RPC2 request
|
|
||||||
|
|
||||||
:returns: an utf8 encoded JSON-RPC2 reply
|
:returns: an utf8 encoded JSON-RPC2 or JSONP reply
|
||||||
"""
|
"""
|
||||||
|
args = self.httprequest.args
|
||||||
|
jsonp = args.get('jsonp', False)
|
||||||
|
requestf = None
|
||||||
|
request = None
|
||||||
|
|
||||||
|
if jsonp and self.httprequest.method == 'POST':
|
||||||
|
# jsonp 2 steps step1 POST: save call
|
||||||
|
self.init(args)
|
||||||
|
req.session.jsonp_requests[args.get('id')] = self.httprequest.form['r']
|
||||||
|
headers=[('Content-Type', 'text/plain; charset=utf-8')]
|
||||||
|
r = werkzeug.wrappers.Response(request_id, headers=headers)
|
||||||
|
return r
|
||||||
|
elif jsonp and args.get('id'):
|
||||||
|
# jsonp 2 steps step2 GET: run and return result
|
||||||
|
self.init(args)
|
||||||
|
request = self.session.jsonp_requests.pop(args.get(id), "")
|
||||||
|
elif jsonp and args.get('r'):
|
||||||
|
# jsonp method GET
|
||||||
|
request = args.get('r')
|
||||||
|
else:
|
||||||
|
# regular jsonrpc2
|
||||||
|
requestf = self.httprequest.stream
|
||||||
|
|
||||||
response = {"jsonrpc": "2.0" }
|
response = {"jsonrpc": "2.0" }
|
||||||
error = None
|
error = None
|
||||||
try:
|
try:
|
||||||
|
@ -188,10 +209,16 @@ class JsonRequest(WebRequest):
|
||||||
|
|
||||||
if _logger.isEnabledFor(logging.DEBUG):
|
if _logger.isEnabledFor(logging.DEBUG):
|
||||||
_logger.debug("<--\n%s", pprint.pformat(response))
|
_logger.debug("<--\n%s", pprint.pformat(response))
|
||||||
content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
|
|
||||||
return werkzeug.wrappers.Response(
|
if jsonp:
|
||||||
content, headers=[('Content-Type', 'application/json'),
|
mime = 'application/javascript'
|
||||||
('Content-Length', len(content))])
|
body = "%s(%s);" % (jsonp, simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder),)
|
||||||
|
else:
|
||||||
|
mime = 'application/json'
|
||||||
|
body = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
|
||||||
|
|
||||||
|
r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
|
||||||
|
return r
|
||||||
|
|
||||||
def jsonrequest(f):
|
def jsonrequest(f):
|
||||||
""" Decorator marking the decorated method as being a handler for a
|
""" Decorator marking the decorated method as being a handler for a
|
||||||
|
@ -205,8 +232,7 @@ def jsonrequest(f):
|
||||||
"""
|
"""
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
def json_handler(controller, request, config):
|
def json_handler(controller, request, config):
|
||||||
return JsonRequest(request, config).dispatch(
|
return JsonRequest(request, config).dispatch(controller, f)
|
||||||
controller, f, requestf=request.stream)
|
|
||||||
json_handler.exposed = True
|
json_handler.exposed = True
|
||||||
return json_handler
|
return json_handler
|
||||||
|
|
||||||
|
@ -281,13 +307,15 @@ STORES = {}
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def session_context(request, storage_path, session_cookie='sessionid'):
|
def session_context(request, storage_path, session_cookie='sessionid'):
|
||||||
session_store = STORES.get(storage_path)
|
session_store, session_lock = STORES.get(storage_path, (None, None))
|
||||||
if not session_store:
|
if not session_store:
|
||||||
session_store = werkzeug.contrib.sessions.FilesystemSessionStore(
|
session_store = werkzeug.contrib.sessions.FilesystemSessionStore(
|
||||||
storage_path)
|
storage_path)
|
||||||
STORES[storage_path] = session_store
|
session_lock = threading.Lock()
|
||||||
|
STORES[storage_path] = session_store, session_lock
|
||||||
|
|
||||||
sid = request.cookies.get(session_cookie)
|
sid = request.cookies.get(session_cookie)
|
||||||
|
with session_lock:
|
||||||
if sid:
|
if sid:
|
||||||
request.session = session_store.get(sid)
|
request.session = session_store.get(sid)
|
||||||
else:
|
else:
|
||||||
|
@ -300,10 +328,14 @@ def session_context(request, storage_path, session_cookie='sessionid'):
|
||||||
# either by login process or by HTTP requests without an OpenERP
|
# either by login process or by HTTP requests without an OpenERP
|
||||||
# session id, and are generally noise
|
# session id, and are generally noise
|
||||||
for key, value in request.session.items():
|
for key, value in request.session.items():
|
||||||
if isinstance(value, session.OpenERPSession) and not value._uid:
|
if (isinstance(value, session.OpenERPSession)
|
||||||
|
and not value._uid
|
||||||
|
and not value.jsonp_requests
|
||||||
|
):
|
||||||
|
_logger.info('remove session %s: %r', key, value.jsonp_requests)
|
||||||
del request.session[key]
|
del request.session[key]
|
||||||
|
|
||||||
# FIXME: remove this when non-literals disappear
|
with session_lock:
|
||||||
if sid:
|
if sid:
|
||||||
# Re-load sessions from storage and merge non-literal
|
# Re-load sessions from storage and merge non-literal
|
||||||
# contexts and domains (they're indexed by hash of the
|
# contexts and domains (they're indexed by hash of the
|
||||||
|
@ -324,6 +356,12 @@ def session_context(request, storage_path, session_cookie='sessionid'):
|
||||||
and v != stored:
|
and v != stored:
|
||||||
v.contexts_store.update(stored.contexts_store)
|
v.contexts_store.update(stored.contexts_store)
|
||||||
v.domains_store.update(stored.domains_store)
|
v.domains_store.update(stored.domains_store)
|
||||||
|
v.jsonp_requests.update(stored.jsonp_requests)
|
||||||
|
|
||||||
|
# add missing keys
|
||||||
|
for k, v in in_store.iteritems():
|
||||||
|
if k not in request.session:
|
||||||
|
request.session[k] = v
|
||||||
|
|
||||||
session_store.save(request.session)
|
session_store.save(request.session)
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ class OpenERPSession(object):
|
||||||
self.context = {}
|
self.context = {}
|
||||||
self.contexts_store = {}
|
self.contexts_store = {}
|
||||||
self.domains_store = {}
|
self.domains_store = {}
|
||||||
|
self.jsonp_requests = {} # FIXME use a LRU
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
state = dict(self.__dict__)
|
state = dict(self.__dict__)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import simplejson
|
import simplejson
|
||||||
import time
|
import time
|
||||||
|
import urllib2
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
import zlib
|
import zlib
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
@ -242,6 +243,21 @@ class WebClient(openerpweb.Controller):
|
||||||
"version": web.common.release.version
|
"version": web.common.release.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Proxy(openerpweb.Controller):
|
||||||
|
_cp_path = '/web/proxy'
|
||||||
|
|
||||||
|
@openerpweb.jsonrequest
|
||||||
|
def load(self, req, path):
|
||||||
|
#req.config.socket_port
|
||||||
|
#if not re.match('^/[^/]+/static/.*', path):
|
||||||
|
# return werkzeug.exceptions.BadRequest()
|
||||||
|
|
||||||
|
env = req.httprequest.environ
|
||||||
|
port = env['SERVER_PORT']
|
||||||
|
|
||||||
|
o = urllib2.urlopen('http://127.0.0.1:%s%s' % (port, path))
|
||||||
|
return o.read()
|
||||||
|
|
||||||
class Database(openerpweb.Controller):
|
class Database(openerpweb.Controller):
|
||||||
_cp_path = "/web/database"
|
_cp_path = "/web/database"
|
||||||
|
|
||||||
|
@ -359,7 +375,6 @@ class Session(openerpweb.Controller):
|
||||||
|
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def get_session_info(self, req):
|
def get_session_info(self, req):
|
||||||
req.session.assert_valid(force=True)
|
|
||||||
return {
|
return {
|
||||||
"uid": req.session._uid,
|
"uid": req.session._uid,
|
||||||
"context": req.session.get_context() if req.session._uid else False,
|
"context": req.session.get_context() if req.session._uid else False,
|
||||||
|
|
|
@ -629,9 +629,6 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||||
callback: continuation || function() {}
|
callback: continuation || function() {}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
on_logout: function() {
|
|
||||||
this.session.logout();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# */{
|
openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# */{
|
||||||
|
@ -995,6 +992,21 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
this._super(null, element_id);
|
this._super(null, element_id);
|
||||||
openerp.webclient = this;
|
openerp.webclient = this;
|
||||||
|
|
||||||
|
this.notification = new openerp.web.Notification(this);
|
||||||
|
this.loading = new openerp.web.Loading(this);
|
||||||
|
this.crashmanager = new openerp.web.CrashManager();
|
||||||
|
|
||||||
|
this.header = new openerp.web.Header(this);
|
||||||
|
this.login = new openerp.web.Login(this);
|
||||||
|
this.header.on_logout.add(this.on_logout);
|
||||||
|
this.header.on_action.add(this.on_menu_action);
|
||||||
|
|
||||||
|
this._current_state = null;
|
||||||
|
},
|
||||||
|
start: function() {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
var self = this;
|
||||||
|
this.session.bind().then(function() {
|
||||||
var params = {};
|
var params = {};
|
||||||
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
|
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
|
||||||
this.$element.addClass("kitten-mode-activated");
|
this.$element.addClass("kitten-mode-activated");
|
||||||
|
@ -1002,40 +1014,30 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
self.$element.toggleClass('clark-gable');
|
self.$element.toggleClass('clark-gable');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.$element.html(QWeb.render("Interface", params));
|
self.$element.html(QWeb.render("Interface", params));
|
||||||
|
self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu");
|
||||||
|
self.menu.on_action.add(self.on_menu_action);
|
||||||
|
|
||||||
this.notification = new openerp.web.Notification(this);
|
self.notification.prependTo(self.$element);
|
||||||
this.loading = new openerp.web.Loading(this);
|
self.loading.appendTo($('#oe_loading'));
|
||||||
this.crashmanager = new openerp.web.CrashManager();
|
self.header.appendTo($("#oe_header"));
|
||||||
|
self.login.appendTo($('#oe_login'));
|
||||||
this.header = new openerp.web.Header(this);
|
self.menu.start();
|
||||||
this.login = new openerp.web.Login(this);
|
self.login.on_login_invalid();
|
||||||
this.header.on_logout.add(this.login.on_logout);
|
});
|
||||||
this.header.on_action.add(this.on_menu_action);
|
this.session.ready.then(function() {
|
||||||
|
self.login.on_login_valid();
|
||||||
this.session.on_session_invalid.add(this.login.do_ask_login);
|
self.header.do_update();
|
||||||
this.session.on_session_valid.add_last(this.header.do_update);
|
self.menu.do_reload();
|
||||||
this.session.on_session_invalid.add_last(this.header.do_update);
|
if(self.action_manager)
|
||||||
this.session.on_session_valid.add_last(this.on_logged);
|
self.action_manager.stop();
|
||||||
this.session.on_session_invalid.add_last(this.on_logged_out);
|
self.action_manager = new openerp.web.ActionManager(this);
|
||||||
|
self.action_manager.appendTo($("#oe_app"));
|
||||||
this.menu = new openerp.web.Menu(this, "oe_menu", "oe_secondary_menu");
|
self.bind_hashchange();
|
||||||
this.menu.on_action.add(this.on_menu_action);
|
});
|
||||||
|
|
||||||
this._current_state = null;
|
|
||||||
|
|
||||||
},
|
|
||||||
start: function() {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
this.notification.prependTo(this.$element);
|
|
||||||
this.loading.appendTo($('#oe_loading'));
|
|
||||||
this.header.appendTo($("#oe_header"));
|
|
||||||
this.session.start();
|
|
||||||
this.login.appendTo($('#oe_login'));
|
|
||||||
this.menu.start();
|
|
||||||
},
|
},
|
||||||
do_reload: function() {
|
do_reload: function() {
|
||||||
return $.when(this.session.session_restore(),this.menu.do_reload());
|
return $.when(this.session.session_init(),this.menu.do_reload());
|
||||||
},
|
},
|
||||||
do_notify: function() {
|
do_notify: function() {
|
||||||
var n = this.notification;
|
var n = this.notification;
|
||||||
|
@ -1045,24 +1047,10 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
var n = this.notification;
|
var n = this.notification;
|
||||||
n.warn.apply(n, arguments);
|
n.warn.apply(n, arguments);
|
||||||
},
|
},
|
||||||
on_logged: function() {
|
on_logout: function() {
|
||||||
this.menu.do_reload();
|
this.session.session_logout();
|
||||||
if(this.action_manager)
|
this.login.on_login_invalid();
|
||||||
this.action_manager.stop();
|
this.header.do_update();
|
||||||
this.action_manager = new openerp.web.ActionManager(this);
|
|
||||||
this.action_manager.appendTo($("#oe_app"));
|
|
||||||
|
|
||||||
if (openerp._modules_loaded) { // TODO: find better option than this
|
|
||||||
this.bind_hashchange();
|
|
||||||
} else {
|
|
||||||
this.session.on_modules_loaded.add({ // XXX what about a $.Deferred ?
|
|
||||||
callback: $.proxy(this, 'bind_hashchange'),
|
|
||||||
unique: true,
|
|
||||||
position: 'last'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_logged_out: function() {
|
|
||||||
$(window).unbind('hashchange', this.on_hashchange);
|
$(window).unbind('hashchange', this.on_hashchange);
|
||||||
this.do_push_state({});
|
this.do_push_state({});
|
||||||
if(this.action_manager)
|
if(this.action_manager)
|
||||||
|
@ -1105,6 +1093,47 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
openerp.currentScript = function() {
|
||||||
|
var currentScript = document.currentScript;
|
||||||
|
if (!currentScript) {
|
||||||
|
var sc = document.getElementsByTagName('script');
|
||||||
|
currentScript = sc[sc.length-1];
|
||||||
|
}
|
||||||
|
return currentScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
openerp.web.EmbeddedClient = openerp.web.Widget.extend({
|
||||||
|
template: 'EmptyComponent',
|
||||||
|
init: function(action_id, options) {
|
||||||
|
this._super();
|
||||||
|
// TODO take the xmlid of a action instead of its id
|
||||||
|
this.action_id = action_id;
|
||||||
|
this.options = options || {};
|
||||||
|
this.am = new openerp.web.ActionManager(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.am.appendTo(this.$element.addClass('openerp'));
|
||||||
|
|
||||||
|
return this.rpc("/web/action/load", { action_id: this.action_id }, function(result) {
|
||||||
|
var action = result.result;
|
||||||
|
action.flags = _.extend({
|
||||||
|
//views_switcher : false,
|
||||||
|
search_view : false,
|
||||||
|
action_buttons : false,
|
||||||
|
sidebar : false
|
||||||
|
//pager : false
|
||||||
|
}, self.options, action.flags || {});
|
||||||
|
|
||||||
|
self.am.do_action(action);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||||
|
|
|
@ -350,11 +350,19 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
* @param {String} [server] JSON-RPC endpoint hostname
|
* @param {String} [server] JSON-RPC endpoint hostname
|
||||||
* @param {String} [port] JSON-RPC endpoint port
|
* @param {String} [port] JSON-RPC endpoint port
|
||||||
*/
|
*/
|
||||||
init: function(server, port) {
|
init: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.server = (server == undefined) ? location.hostname : server;
|
// TODO: session store in cookie should be optional
|
||||||
this.port = (port == undefined) ? location.port : port;
|
this.name = openerp._session_id;
|
||||||
this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
|
},
|
||||||
|
bind: function(host, protocol) {
|
||||||
|
var self = this;
|
||||||
|
this.host = (host == undefined) ? location.host : host;
|
||||||
|
this.protocol = (protocol == undefined) ? location.protocol : protocol;
|
||||||
|
this.prefix = this.protocol + '//' + this.host;
|
||||||
|
openerp.web.qweb.default_dict['_s'] = this.prefix
|
||||||
|
this.rpc_mode = (this.host == location.host) ? "json" : "jsonp";
|
||||||
|
this.rpc_function = (this.host == location.host) ? this.rpc_json : this.rpc_jsonp;
|
||||||
this.debug = (window.location.search.indexOf('?debug') !== -1);
|
this.debug = (window.location.search.indexOf('?debug') !== -1);
|
||||||
this.session_id = false;
|
this.session_id = false;
|
||||||
this.uid = false;
|
this.uid = false;
|
||||||
|
@ -366,14 +374,8 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
this.context = {};
|
this.context = {};
|
||||||
this.shortcuts = [];
|
this.shortcuts = [];
|
||||||
this.active_id = null;
|
this.active_id = null;
|
||||||
// TODO: session should have an optional name indicating that they'll
|
this.ready = $.Deferred();
|
||||||
// be saved to (and revived from) cookies
|
return this.session_init();
|
||||||
this.name = 'session';
|
|
||||||
this.do_load_qweb(['/web/webclient/qweb']);
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this.session_restore();
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Executes an RPC call, registering the provided callbacks.
|
* Executes an RPC call, registering the provided callbacks.
|
||||||
|
@ -390,82 +392,133 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
*/
|
*/
|
||||||
rpc: function(url, params, success_callback, error_callback) {
|
rpc: function(url, params, success_callback, error_callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
// url can be an $.ajax option object
|
||||||
|
if (_.isString(url)) {
|
||||||
|
url = { url: url };
|
||||||
|
}
|
||||||
// Construct a JSON-RPC2 request, method is currently unused
|
// Construct a JSON-RPC2 request, method is currently unused
|
||||||
params.session_id = this.session_id;
|
params.session_id = this.session_id;
|
||||||
if (this.debug)
|
if (this.debug)
|
||||||
params.debug = 1;
|
params.debug = 1;
|
||||||
|
var payload = {
|
||||||
// Call using the rpc_mode
|
jsonrpc: '2.0',
|
||||||
var deferred = $.Deferred();
|
method: 'call',
|
||||||
this.rpc_ajax(url, {
|
|
||||||
jsonrpc: "2.0",
|
|
||||||
method: "call",
|
|
||||||
params: params,
|
params: params,
|
||||||
id: _.uniqueId('browser-client-')
|
id: _.uniqueId('r')
|
||||||
}).then(function () {deferred.resolve.apply(deferred, arguments);},
|
};
|
||||||
function(error) {deferred.reject(error, $.Event());});
|
|
||||||
return deferred.fail(function() {
|
|
||||||
deferred.fail(function(error, event) {
|
|
||||||
if (!event.isDefaultPrevented()) {
|
|
||||||
self.on_rpc_error(error, event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).then(success_callback, error_callback).promise();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Raw JSON-RPC call
|
|
||||||
*
|
|
||||||
* @returns {jQuery.Deferred} ajax-webd deferred object
|
|
||||||
*/
|
|
||||||
rpc_ajax: function(url, payload) {
|
|
||||||
var self = this;
|
|
||||||
this.on_rpc_request();
|
|
||||||
// url can be an $.ajax option object
|
|
||||||
if (_.isString(url)) {
|
|
||||||
url = {
|
|
||||||
url: url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ajax = _.extend({
|
|
||||||
type: "POST",
|
|
||||||
url: url,
|
|
||||||
dataType: 'json',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(payload),
|
|
||||||
processData: false
|
|
||||||
}, url);
|
|
||||||
var deferred = $.Deferred();
|
var deferred = $.Deferred();
|
||||||
$.ajax(ajax).done(function(response, textStatus, jqXHR) {
|
this.on_rpc_request();
|
||||||
|
this.rpc_function(url, payload).then(
|
||||||
|
function (response, textStatus, jqXHR) {
|
||||||
self.on_rpc_response();
|
self.on_rpc_response();
|
||||||
if (!response.error) {
|
if (!response.error) {
|
||||||
deferred.resolve(response["result"], textStatus, jqXHR);
|
deferred.resolve(response["result"], textStatus, jqXHR);
|
||||||
return;
|
} else if (response.error.data.type === "session_invalid") {
|
||||||
}
|
|
||||||
if (response.error.data.type !== "session_invalid") {
|
|
||||||
deferred.reject(response.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.uid = false;
|
self.uid = false;
|
||||||
|
// TODO deprecate or use a deferred on login.do_ask_login()
|
||||||
self.on_session_invalid(function() {
|
self.on_session_invalid(function() {
|
||||||
self.rpc(url, payload.params,
|
self.rpc(url, payload.params,
|
||||||
function() {
|
function() { deferred.resolve.apply(deferred, arguments); },
|
||||||
deferred.resolve.apply(deferred, arguments);
|
function() { deferred.reject.apply(deferred, arguments); });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred.reject(response.error, $.Event());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
function(error, event) {
|
function(jqXHR, textStatus, errorThrown) {
|
||||||
event.preventDefault();
|
|
||||||
deferred.reject.apply(deferred, arguments);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
|
||||||
self.on_rpc_response();
|
self.on_rpc_response();
|
||||||
var error = {
|
var error = {
|
||||||
code: -32098,
|
code: -32098,
|
||||||
message: "XmlHttpRequestError " + errorThrown,
|
message: "XmlHttpRequestError " + errorThrown,
|
||||||
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
|
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
|
||||||
};
|
};
|
||||||
deferred.reject(error);
|
deferred.reject(error, $.Event());
|
||||||
});
|
});
|
||||||
return deferred.promise();
|
// Allow deferred user to disable on_rpc_error in fail
|
||||||
|
deferred.fail(function() {
|
||||||
|
deferred.fail(function(error, event) {
|
||||||
|
if (!event.isDefaultPrevented()) {
|
||||||
|
self.on_rpc_error(error, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).then(success_callback, error_callback).promise();
|
||||||
|
return deferred;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Raw JSON-RPC call
|
||||||
|
*
|
||||||
|
* @returns {jQuery.Deferred} ajax-webd deferred object
|
||||||
|
*/
|
||||||
|
rpc_json: function(url, payload) {
|
||||||
|
var self = this;
|
||||||
|
var ajax = _.extend({
|
||||||
|
type: "POST",
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(payload),
|
||||||
|
processData: false,
|
||||||
|
}, url);
|
||||||
|
return $.ajax(ajax);
|
||||||
|
},
|
||||||
|
rpc_jsonp: function(url, payload) {
|
||||||
|
var self = this;
|
||||||
|
// extracted from payload to set on the url
|
||||||
|
var data = {
|
||||||
|
session_id: this.session_id,
|
||||||
|
id: payload.id,
|
||||||
|
};
|
||||||
|
url.url = this.get_url(url.url);
|
||||||
|
var ajax = _.extend({
|
||||||
|
type: "GET",
|
||||||
|
dataType: 'jsonp',
|
||||||
|
jsonp: 'jsonp',
|
||||||
|
cache: false,
|
||||||
|
data: data
|
||||||
|
}, url);
|
||||||
|
var payload_str = JSON.stringify(payload);
|
||||||
|
var payload_url = $.param({r:payload_str});
|
||||||
|
if(playload_url.length < 2000) {
|
||||||
|
// Direct jsonp request
|
||||||
|
ajax.data.r = payload_str;
|
||||||
|
return $.ajax(ajax);
|
||||||
|
} else {
|
||||||
|
// Indirect jsonp request
|
||||||
|
var ifid = _.uniqueId('oe_rpc_iframe');
|
||||||
|
var display = options.openerp.debug ? 'block' : 'none';
|
||||||
|
var $iframe = $(_.str.sprintf("<iframe src='javascript:false;' name='%s' id='%s' style='display:%s'></iframe>", ifid, ifid, display));
|
||||||
|
var $form = $('<form>')
|
||||||
|
.attr('method', 'POST')
|
||||||
|
.attr('target', ifid)
|
||||||
|
.attr('enctype', "multipart/form-data")
|
||||||
|
.attr('action', ajax.url + '?' + $.param(data))
|
||||||
|
.append($('<input type="hidden" name="r" />').attr('value', payload_str))
|
||||||
|
.hide()
|
||||||
|
.appendTo($('body'));
|
||||||
|
var cleanUp = function() {
|
||||||
|
if ($iframe) {
|
||||||
|
$iframe.unbind("load").attr("src", "javascript:false;").remove();
|
||||||
|
}
|
||||||
|
$form.remove();
|
||||||
|
};
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
// the first bind is fired up when the iframe is added to the DOM
|
||||||
|
$iframe.bind('load', function() {
|
||||||
|
// the second bind is fired up when the result of the form submission is received
|
||||||
|
$iframe.unbind('load').bind('load', function() {
|
||||||
|
$.ajax(ajax).always(function() {
|
||||||
|
cleanUp();
|
||||||
|
}).then(
|
||||||
|
function() { deferred.resolve.apply(deferred, arguments); },
|
||||||
|
function() { deferred.reject.apply(deferred, arguments); }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// now that the iframe can receive data, we fill and submit the form
|
||||||
|
$form.submit();
|
||||||
|
});
|
||||||
|
// append the iframe to the DOM (will trigger the first load)
|
||||||
|
$form.after($iframe);
|
||||||
|
return deferred;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
on_rpc_request: function() {
|
on_rpc_request: function() {
|
||||||
},
|
},
|
||||||
|
@ -473,76 +526,52 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
},
|
},
|
||||||
on_rpc_error: function(error) {
|
on_rpc_error: function(error) {
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Init a session, reloads from cookie, if it exists
|
||||||
|
*/
|
||||||
|
session_init: function () {
|
||||||
|
var self = this;
|
||||||
|
// TODO: session store in cookie should be optional
|
||||||
|
this.session_id = this.get_cookie('session_id');
|
||||||
|
return this.rpc("/web/session/get_session_info", {}).pipe(function(result) {
|
||||||
|
// If immediately follows a login (triggered by trying to restore
|
||||||
|
// an invalid session or no session at all), refresh session data
|
||||||
|
// (should not change, but just in case...)
|
||||||
|
_.extend(self, {
|
||||||
|
db: result.db,
|
||||||
|
username: result.login,
|
||||||
|
uid: result.uid,
|
||||||
|
user_context: result.context
|
||||||
|
});
|
||||||
|
var deferred = self.do_load_qweb(['/web/webclient/qweb']);
|
||||||
|
if(self.uid) {
|
||||||
|
return deferred.then(self.load_modules());
|
||||||
|
}
|
||||||
|
return deferred;
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* The session is validated either by login or by restoration of a previous session
|
* The session is validated either by login or by restoration of a previous session
|
||||||
*/
|
*/
|
||||||
on_session_valid: function() {
|
session_authenticate: function(db, login, password) {
|
||||||
if(!openerp._modules_loaded)
|
|
||||||
this.load_modules();
|
|
||||||
},
|
|
||||||
on_session_invalid: function(contination) {
|
|
||||||
},
|
|
||||||
session_is_valid: function() {
|
|
||||||
return this.uid;
|
|
||||||
},
|
|
||||||
session_authenticate: function(db, login, password, success_callback) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var base_location = document.location.protocol + '//' + document.location.host;
|
var base_location = document.location.protocol + '//' + document.location.host;
|
||||||
var params = { db: db, login: login, password: password, base_location: base_location };
|
var params = { db: db, login: login, password: password, base_location: base_location };
|
||||||
return this.rpc("/web/session/authenticate", params, function(result) {
|
return this.rpc("/web/session/authenticate", params).pipe(function(result) {
|
||||||
_.extend(self, {
|
_.extend(self, {
|
||||||
session_id: result.session_id,
|
session_id: result.session_id,
|
||||||
uid: result.uid,
|
|
||||||
user_context: result.context,
|
|
||||||
db: result.db,
|
db: result.db,
|
||||||
username: result.login
|
username: result.login,
|
||||||
});
|
|
||||||
self.session_save();
|
|
||||||
self.on_session_valid();
|
|
||||||
return true;
|
|
||||||
}).then(success_callback);
|
|
||||||
},
|
|
||||||
login: function() { this.session_authenticate.apply(this, arguments); },
|
|
||||||
/**
|
|
||||||
* Reloads uid and session_id from local storage, if they exist
|
|
||||||
*/
|
|
||||||
session_restore: function () {
|
|
||||||
var self = this;
|
|
||||||
this.session_id = this.get_cookie('session_id');
|
|
||||||
return this.rpc("/web/session/get_session_info", {}).then(function(result) {
|
|
||||||
// If immediately follows a login (triggered by trying to restore
|
|
||||||
// an invalid session or no session at all), refresh session data
|
|
||||||
// (should not change, but just in case...) but should not call
|
|
||||||
// on_session_valid again as it triggers reloading the menu
|
|
||||||
var already_logged = self.uid;
|
|
||||||
_.extend(self, {
|
|
||||||
uid: result.uid,
|
uid: result.uid,
|
||||||
user_context: result.context,
|
user_context: result.context
|
||||||
db: result.db,
|
|
||||||
username: result.login
|
|
||||||
});
|
});
|
||||||
if (!already_logged) {
|
// TODO: session store in cookie should be optional
|
||||||
if (self.uid) {
|
self.set_cookie('session_id', self.session_id);
|
||||||
self.on_session_valid();
|
return self.load_modules();
|
||||||
} else {
|
|
||||||
self.on_session_invalid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, function() {
|
|
||||||
self.on_session_invalid();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
session_logout: function() {
|
||||||
* Saves the session id and uid locally
|
|
||||||
*/
|
|
||||||
session_save: function () {
|
|
||||||
this.set_cookie('session_id', this.session_id);
|
|
||||||
},
|
|
||||||
logout: function() {
|
|
||||||
this.set_cookie('session_id', '');
|
this.set_cookie('session_id', '');
|
||||||
this.reload_client();
|
|
||||||
},
|
|
||||||
reload_client: function() {
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -586,23 +615,28 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
*/
|
*/
|
||||||
load_modules: function() {
|
load_modules: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
if(openerp._modules_loaded) {
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
this.rpc('/web/session/modules', {}, function(result) {
|
this.rpc('/web/session/modules', {}, function(result) {
|
||||||
self.module_list = result;
|
self.module_list = result;
|
||||||
var lang = self.user_context.lang;
|
var lang = self.user_context.lang;
|
||||||
var params = { mods: ["web"].concat(result), lang: lang};
|
var params = { mods: ["web"].concat(result), lang: lang};
|
||||||
self.rpc('/web/webclient/translations',params).then(function(transs) {
|
self.rpc('/web/webclient/translations',params).pipe(function(transs) {
|
||||||
openerp.web._t.database.set_bundle(transs);
|
openerp.web._t.database.set_bundle(transs);
|
||||||
var modules = self.module_list.join(',');
|
var modules = self.module_list.join(',');
|
||||||
var file_list = ["/web/static/lib/datejs/globalization/" +
|
var file_list = ["/web/static/lib/datejs/globalization/" +
|
||||||
self.user_context.lang.replace("_", "-") + ".js"
|
self.user_context.lang.replace("_", "-") + ".js"
|
||||||
];
|
];
|
||||||
|
return $.when(
|
||||||
self.rpc('/web/webclient/csslist', {"mods": modules}, self.do_load_css);
|
self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css),
|
||||||
self.rpc('/web/webclient/jslist', {"mods": modules}, function(files) {
|
self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb),
|
||||||
self.do_load_js(file_list.concat(files));
|
self.rpc('/web/webclient/jslist', {mods: modules}).pipe(function(files) {
|
||||||
|
return self.do_load_js(file_list.concat(files));
|
||||||
|
})
|
||||||
|
).then(function() {
|
||||||
|
self.ready.resolve();
|
||||||
});
|
});
|
||||||
self.rpc('/web/webclient/qweblist', {"mods": modules}, self.do_load_qweb);
|
|
||||||
openerp._modules_loaded = true;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -610,7 +644,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
var self = this;
|
var self = this;
|
||||||
_.each(files, function (file) {
|
_.each(files, function (file) {
|
||||||
$('head').append($('<link>', {
|
$('head').append($('<link>', {
|
||||||
'href': file,
|
'href': self.get_url(file),
|
||||||
'rel': 'stylesheet',
|
'rel': 'stylesheet',
|
||||||
'type': 'text/css'
|
'type': 'text/css'
|
||||||
}));
|
}));
|
||||||
|
@ -618,28 +652,39 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
},
|
},
|
||||||
do_load_js: function(files) {
|
do_load_js: function(files) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var d = $.Deferred();
|
||||||
if(files.length != 0) {
|
if(files.length != 0) {
|
||||||
var file = files.shift();
|
var file = files.shift();
|
||||||
var tag = document.createElement('script');
|
var tag = document.createElement('script');
|
||||||
tag.type = 'text/javascript';
|
tag.type = 'text/javascript';
|
||||||
tag.src = file;
|
tag.src = self.get_url(file);
|
||||||
tag.onload = tag.onreadystatechange = function() {
|
tag.onload = tag.onreadystatechange = function() {
|
||||||
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
|
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
|
||||||
return;
|
return;
|
||||||
tag.onload_done = true;
|
tag.onload_done = true;
|
||||||
self.do_load_js(files);
|
self.do_load_js(files).then(function () {
|
||||||
|
d.resolve();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
var head = document.head || document.getElementsByTagName('head')[0];
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
head.appendChild(tag);
|
head.appendChild(tag);
|
||||||
} else {
|
} else {
|
||||||
this.on_modules_loaded();
|
self.on_modules_loaded();
|
||||||
|
d.resolve();
|
||||||
}
|
}
|
||||||
|
return d;
|
||||||
},
|
},
|
||||||
do_load_qweb: function(files) {
|
do_load_qweb: function(files) {
|
||||||
var self = this;
|
var self = this;
|
||||||
_.each(files, function(file) {
|
if (files.length != 0) {
|
||||||
openerp.web.qweb.add_template(file);
|
var file = files.shift();
|
||||||
|
return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
|
||||||
|
openerp.web.qweb.add_template(_.str.trim(xml));
|
||||||
|
return self.do_load_qweb(files);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
on_modules_loaded: function() {
|
on_modules_loaded: function() {
|
||||||
for(var j=0; j<this.module_list.length; j++) {
|
for(var j=0; j<this.module_list.length; j++) {
|
||||||
|
@ -654,6 +699,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
get_url: function (file) {
|
||||||
|
return this.prefix + file;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Cooperative file download implementation, for ajaxy APIs.
|
* Cooperative file download implementation, for ajaxy APIs.
|
||||||
*
|
*
|
||||||
|
@ -1069,6 +1117,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Configure blockui */
|
||||||
if ($.blockUI) {
|
if ($.blockUI) {
|
||||||
$.blockUI.defaults.baseZ = 1100;
|
$.blockUI.defaults.baseZ = 1100;
|
||||||
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
|
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
|
||||||
|
@ -1100,6 +1149,7 @@ openerp.web.qweb.format_text_node = function(s) {
|
||||||
openerp.connection = new openerp.web.Connection();
|
openerp.connection = new openerp.web.Connection();
|
||||||
openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
|
openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
|
||||||
|
|
||||||
|
/** Jquery extentions */
|
||||||
$.Mutex = (function() {
|
$.Mutex = (function() {
|
||||||
function Mutex() {
|
function Mutex() {
|
||||||
this.def = $.Deferred().resolve();
|
this.def = $.Deferred().resolve();
|
||||||
|
|
|
@ -254,7 +254,7 @@ openerp.web.format_cell = function (row_data, column, value_if_empty, process_mo
|
||||||
if (column.tag === 'button') {
|
if (column.tag === 'button') {
|
||||||
return [
|
return [
|
||||||
'<button type="button" title="', column.string || '', '">',
|
'<button type="button" title="', column.string || '', '">',
|
||||||
'<img src="/web/static/src/img/icons/', column.icon, '.png"',
|
'<img src="', openerp.connection.prefix, '/web/static/src/img/icons/', column.icon, '.png"',
|
||||||
' alt="', column.string || '', '"/>',
|
' alt="', column.string || '', '"/>',
|
||||||
'</button>'
|
'</button>'
|
||||||
].join('')
|
].join('')
|
||||||
|
|
|
@ -266,7 +266,7 @@
|
||||||
<form class="oe_forms">
|
<form class="oe_forms">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend style="">
|
<legend style="">
|
||||||
<img src="/web/static/src/img/stock_person.png" alt="" />
|
<img t-att-src='_s + "/web/static/src/img/stock_person.png"' alt="" />
|
||||||
</legend>
|
</legend>
|
||||||
<div class="oe_box2">
|
<div class="oe_box2">
|
||||||
<table align="center" cellspacing="2px" cellpadding="0">
|
<table align="center" cellspacing="2px" cellpadding="0">
|
||||||
|
@ -306,7 +306,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="/web/static/src/img/product.png"/>
|
<img t-att-src='_s + "/web/static/src/img/product.png"'/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>Full featured</strong><br />
|
<strong>Full featured</strong><br />
|
||||||
|
@ -315,7 +315,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="/web/static/src/img/accessories-archiver.png"/>
|
<img t-att-src='_s + "/web/static/src/img/accessories-archiver.png"'/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>Open Source</strong><br />
|
<strong>Open Source</strong><br />
|
||||||
|
@ -324,7 +324,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="/web/static/src/img/partner.png" />
|
<img t-att-src='_s + "/web/static/src/img/partner.png"' />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>User Friendly</strong><br />
|
<strong>User Friendly</strong><br />
|
||||||
|
@ -352,13 +352,13 @@
|
||||||
<div class="header_corner">
|
<div class="header_corner">
|
||||||
<ul class="block">
|
<ul class="block">
|
||||||
<li>
|
<li>
|
||||||
<a t-att-href="'/' + widget.qs" title="Home" class="home"><img src="/web/static/src/img/header-home.png" width="16" height="16" border="0"/></a>
|
<a t-att-href="'/' + widget.qs" title="Home" class="home"><img t-att-src='_s + "/web/static/src/img/header-home.png"' width="16" height="16" border="0"/></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="preferences">
|
<li class="preferences">
|
||||||
<a href="javascript:void(0)" title="Preferences" class="preferences"><img src="/web/static/src/img/header-preferences.png" width="16" height="16" border="0"/></a>
|
<a href="javascript:void(0)" title="Preferences" class="preferences"><img t-att-src='_s + "/web/static/src/img/header-preferences.png"' width="16" height="16" border="0"/></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:void(0)" title="About" class="about"><img src="/web/static/src/img/header-about.png" width="16" height="16" border="0"/></a>
|
<a href="javascript:void(0)" title="About" class="about"><img t-att-src='_s + "/web/static/src/img/header-about.png"' width="16" height="16" border="0"/></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
|
@ -724,7 +724,7 @@
|
||||||
<input type="hidden" name="model" t-att-value="view.dataset.model"/>
|
<input type="hidden" name="model" t-att-value="view.dataset.model"/>
|
||||||
<input type="hidden" name="id" t-att-value="view.datarecord.id"/>
|
<input type="hidden" name="id" t-att-value="view.datarecord.id"/>
|
||||||
<button class="button" type="button">
|
<button class="button" type="button">
|
||||||
<img src="/web/static/src/img/throbber.gif" width="16" height="16" style="display: none"/>
|
<img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/>
|
||||||
<span>Add</span>
|
<span>Add</span>
|
||||||
</button>
|
</button>
|
||||||
<input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
|
<input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
|
||||||
|
@ -736,14 +736,14 @@
|
||||||
<br style="clear: both"/>
|
<br style="clear: both"/>
|
||||||
<ul class="oe-sidebar-attachments-items">
|
<ul class="oe-sidebar-attachments-items">
|
||||||
<li t-foreach="attachments" t-as="attachment">
|
<li t-foreach="attachments" t-as="attachment">
|
||||||
<t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="'/web/binary/saveas?session_id='
|
<t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id='
|
||||||
+ session.session_id + '&model=ir.attachment&id=' + attachment.id
|
+ session.session_id + '&model=ir.attachment&id=' + attachment.id
|
||||||
+ '&field=datas&fieldname=name&t=' + (new Date().getTime())"/>
|
+ '&field=datas&fieldname=name&t=' + (new Date().getTime())"/>
|
||||||
<a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
|
<a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
|
||||||
<t t-esc="attachment.name"/>
|
<t t-esc="attachment.name"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}">
|
<a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}">
|
||||||
<img src="/web/static/src/img/attachments-close.png" width="15" height="15" border="0"/>
|
<img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -895,7 +895,7 @@
|
||||||
t-att-id="widget.element_id"
|
t-att-id="widget.element_id"
|
||||||
t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}"
|
t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
/><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="FieldChar.readonly">
|
<t t-name="FieldChar.readonly">
|
||||||
<div
|
<div
|
||||||
|
@ -914,7 +914,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td width="16">
|
<td width="16">
|
||||||
<button type="button" class="button" title="Send an e-mail with your default e-mail client">
|
<button type="button" class="button" title="Send an e-mail with your default e-mail client">
|
||||||
<img src="/web/static/src/img/icons/terp-mail-message-new.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -928,7 +928,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td width="16">
|
<td width="16">
|
||||||
<button type="button" class="button" title="Open this resource">
|
<button type="button" class="button" title="Open this resource">
|
||||||
<img src="/web/static/src/img/icons/gtk-ok.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -940,7 +940,7 @@
|
||||||
t-att-id="widget.element_id"
|
t-att-id="widget.element_id"
|
||||||
t-attf-class="field_#{widget.type}"
|
t-attf-class="field_#{widget.type}"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
></textarea><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
></textarea><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="web.datetimepicker">
|
<t t-name="web.datetimepicker">
|
||||||
<div class="oe_datepicker_root">
|
<div class="oe_datepicker_root">
|
||||||
|
@ -948,7 +948,7 @@
|
||||||
<input type="text" size="1" style="width: 100%"
|
<input type="text" size="1" style="width: 100%"
|
||||||
t-att-name="widget.name"
|
t-att-name="widget.name"
|
||||||
t-attf-class="oe_datepicker_master field_#{widget.type_of_date}"
|
t-attf-class="oe_datepicker_master field_#{widget.type_of_date}"
|
||||||
/><img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
|
/><img class="oe_input_icon oe_datepicker_trigger" t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
|
||||||
title="Select date" width="16" height="16" border="0"/>
|
title="Select date" width="16" height="16" border="0"/>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
@ -968,9 +968,9 @@
|
||||||
<input type="text" size="1" style="width: 100%;"
|
<input type="text" size="1" style="width: 100%;"
|
||||||
t-att-id="widget.element_id"/>
|
t-att-id="widget.element_id"/>
|
||||||
<span class="oe-m2o-drop-down-button">
|
<span class="oe-m2o-drop-down-button">
|
||||||
<img src="/web/static/src/img/down-arrow.png" /></span>
|
<img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
|
||||||
<span class="oe-m2o-cm-button" t-att-id="widget.name + '_open'">
|
<span class="oe-m2o-cm-button" t-att-id="widget.name + '_open'">
|
||||||
<img src="/web/static/src/img/icons/gtk-index.png"/></span>
|
<img t-att-src='_s + "/web/static/src/img/icons/gtk-index.png"'/></span>
|
||||||
<div t-att-id="widget.cm_id" class="contextMenu" style="display:none">
|
<div t-att-id="widget.cm_id" class="contextMenu" style="display:none">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1021,7 +1021,7 @@
|
||||||
<table cellpadding="0" cellspacing="0" border="0">
|
<table cellpadding="0" cellspacing="0" border="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<img src="/web/static/src/img/placeholder.png" class="oe-binary-image"
|
<img t-att-src='_s + "/web/static/src/img/placeholder.png"' class="oe-binary-image"
|
||||||
t-att-border="widget.readonly ? 0 : 1"
|
t-att-border="widget.readonly ? 0 : 1"
|
||||||
t-att-id="widget.element_id + '_field'"
|
t-att-id="widget.element_id + '_field'"
|
||||||
t-att-name="widget.name"
|
t-att-name="widget.name"
|
||||||
|
@ -1043,7 +1043,7 @@
|
||||||
<input type="hidden" name="session_id" value=""/>
|
<input type="hidden" name="session_id" value=""/>
|
||||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||||
<button class="button" type="button" title="Set Image">
|
<button class="button" type="button" title="Set Image">
|
||||||
<img src="/web/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
|
||||||
</button>
|
</button>
|
||||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1051,14 +1051,14 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||||
<img src="/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe-binary-progress" style="display: none">
|
<div class="oe-binary-progress" style="display: none">
|
||||||
<img src="/web/static/src/img/throbber.gif" width="16" height="16"/>
|
<img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
|
||||||
<b>Uploading ...</b>
|
<b>Uploading ...</b>
|
||||||
</div>
|
</div>
|
||||||
<iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"> </iframe>
|
<iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"> </iframe>
|
||||||
|
@ -1086,7 +1086,7 @@
|
||||||
<input type="hidden" name="session_id" value=""/>
|
<input type="hidden" name="session_id" value=""/>
|
||||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||||
<button class="button" type="button" title="Set Image">
|
<button class="button" type="button" title="Set Image">
|
||||||
<img src="/web/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
|
||||||
<span>Select</span>
|
<span>Select</span>
|
||||||
</button>
|
</button>
|
||||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||||
|
@ -1095,13 +1095,13 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="button oe-binary-file-save" type="button" title="Save As">
|
<button class="button oe-binary-file-save" type="button" title="Save As">
|
||||||
<img src="/web/static/src/img/icons/gtk-save.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/>
|
||||||
<span>Save As</span>
|
<span>Save As</span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||||
<img src="/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
|
||||||
<span>Clear</span>
|
<span>Clear</span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1109,7 +1109,7 @@
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
<td class="oe-binary-progress" style="display: none" nowrap="true">
|
<td class="oe-binary-progress" style="display: none" nowrap="true">
|
||||||
<img src="/web/static/src/img/throbber.gif" width="16" height="16"/>
|
<img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
|
||||||
<b>Uploading ...</b>
|
<b>Uploading ...</b>
|
||||||
<iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"> </iframe>
|
<iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"> </iframe>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1118,7 +1118,7 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="WidgetButton">
|
<t t-name="WidgetButton">
|
||||||
<button type="button" class="oe_button">
|
<button type="button" class="oe_button">
|
||||||
<img t-if="widget.node.attrs.icon" t-att-src="'/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
|
<img t-if="widget.node.attrs.icon" t-att-src="_s + '/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
|
||||||
<span t-if="widget.string"><t t-esc="widget.string"/></span>
|
<span t-if="widget.string"><t t-esc="widget.string"/></span>
|
||||||
</button>
|
</button>
|
||||||
</t>
|
</t>
|
||||||
|
@ -1211,7 +1211,7 @@
|
||||||
t-att-title="attrs.help"
|
t-att-title="attrs.help"
|
||||||
t-att-class="classes.join(' ')"
|
t-att-class="classes.join(' ')"
|
||||||
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined">
|
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined">
|
||||||
<img t-att-src="'/web/static/src/img/icons/' + (attrs.icon || 'gtk-home') + '.png'" width="16" height="16"/>
|
<img t-att-src="_s + '/web/static/src/img/icons/' + (attrs.icon || 'gtk-home') + '.png'" width="16" height="16"/>
|
||||||
<br t-if="attrs.string"/>
|
<br t-if="attrs.string"/>
|
||||||
<t t-esc="attrs.string"/>
|
<t t-esc="attrs.string"/>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1358,7 +1358,7 @@
|
||||||
<t t-name="DialogWarning">
|
<t t-name="DialogWarning">
|
||||||
<table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
|
<table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
|
||||||
<tr>
|
<tr>
|
||||||
<td><img src="/web/static/src/img/warning.png" class="oe-dialog-icon"/></td>
|
<td><img t-att-src='_s + "/web/static/src/img/warning.png"' class="oe-dialog-icon"/></td>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
<t t-js="d">
|
<t t-js="d">
|
||||||
|
@ -1586,7 +1586,7 @@
|
||||||
<td valign="top" align="left" style="cursor: pointer;" width="18">
|
<td valign="top" align="left" style="cursor: pointer;" width="18">
|
||||||
<t t-if="field.children">
|
<t t-if="field.children">
|
||||||
<t t-if="(field.id).split('/').length != 3">
|
<t t-if="(field.id).split('/').length != 3">
|
||||||
<img t-att-id="'parentimg-' + field.id" src="/web/static/src/img/expand.gif" width="16" height="16" border="0"/>
|
<img t-att-id="'parentimg-' + field.id" t-att-src='_s + "/web/static/src/img/expand.gif"' width="16" height="16" border="0"/>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1702,7 +1702,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td t-foreach="records[0]" t-as="column">
|
<td t-foreach="records[0]" t-as="column">
|
||||||
<input class="sel_fields" placeholder="--- Don't Import ---"/><span class="oe-m2o-drop-down-button">
|
<input class="sel_fields" placeholder="--- Don't Import ---"/><span class="oe-m2o-drop-down-button">
|
||||||
<img src="/web/static/src/img/down-arrow.png" /></span>
|
<img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr t-foreach="records" t-as="record" class="oe_import_grid-row">
|
<tr t-foreach="records" t-as="record" class="oe_import_grid-row">
|
||||||
|
|
|
@ -106,7 +106,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||||
node.children = [{
|
node.children = [{
|
||||||
tag: 'img',
|
tag: 'img',
|
||||||
attrs: {
|
attrs: {
|
||||||
src: '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png',
|
src: openerp.connection.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png',
|
||||||
width: '16',
|
width: '16',
|
||||||
height: '16'
|
height: '16'
|
||||||
}
|
}
|
||||||
|
@ -524,7 +524,7 @@ openerp.web_kanban.KanbanRecord = openerp.web.Widget.extend({
|
||||||
},
|
},
|
||||||
kanban_image: function(model, field, id) {
|
kanban_image: function(model, field, id) {
|
||||||
id = id || '';
|
id = id || '';
|
||||||
return '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id;
|
return openerp.connection.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id;
|
||||||
},
|
},
|
||||||
kanban_text_ellipsis: function(s, size) {
|
kanban_text_ellipsis: function(s, size) {
|
||||||
size = size || 160;
|
size = size || 160;
|
||||||
|
|
Loading…
Reference in New Issue