[IMP] Sets the request object in web controlles as a global object

bzr revid: nicolas.vanhoren@openerp.com-20130607121905-dq474wdsppatfv9c
This commit is contained in:
niv-openerp 2013-06-07 14:19:05 +02:00
commit 901ba0cba5
4 changed files with 216 additions and 168 deletions

View File

@ -36,6 +36,8 @@ from openerp.tools.translate import _
from .. import http from .. import http
openerpweb = http openerpweb = http
from openerp.addons.web.http import request as req
#---------------------------------------------------------- #----------------------------------------------------------
# OpenERP Web helpers # OpenERP Web helpers
#---------------------------------------------------------- #----------------------------------------------------------
@ -84,7 +86,7 @@ def rjsmin(script):
).strip() ).strip()
return result return result
def db_list(req): def db_list():
proxy = req.session.proxy("db") proxy = req.session.proxy("db")
dbs = proxy.list() dbs = proxy.list()
h = req.httprequest.environ['HTTP_HOST'].split(':')[0] h = req.httprequest.environ['HTTP_HOST'].split(':')[0]
@ -93,7 +95,7 @@ def db_list(req):
dbs = [i for i in dbs if re.match(r, i)] dbs = [i for i in dbs if re.match(r, i)]
return dbs return dbs
def db_monodb_redirect(req): def db_monodb_redirect():
db = False db = False
redirect = False redirect = False
@ -103,7 +105,7 @@ def db_monodb_redirect(req):
return (db_url, False) return (db_url, False)
try: try:
dbs = db_list(req) dbs = db_list()
except Exception: except Exception:
# ignore access denied # ignore access denied
dbs = [] dbs = []
@ -124,11 +126,11 @@ def db_monodb_redirect(req):
redirect = req.httprequest.path + '?' + urllib.urlencode(query) redirect = req.httprequest.path + '?' + urllib.urlencode(query)
return (db, redirect) return (db, redirect)
def db_monodb(req): def db_monodb():
# if only one db exists, return it else return False # if only one db exists, return it else return False
return db_monodb_redirect(req)[0] return db_monodb_redirect()[0]
def redirect_with_hash(req, url, code=303): def redirect_with_hash(url, code=303):
if req.httprequest.user_agent.browser == 'msie': if req.httprequest.user_agent.browser == 'msie':
try: try:
version = float(req.httprequest.user_agent.version) version = float(req.httprequest.user_agent.version)
@ -182,7 +184,7 @@ def module_topological_sort(modules):
visit(n) visit(n)
return L return L
def module_installed(req): def module_installed():
# Candidates module the current heuristic is the /static dir # Candidates module the current heuristic is the /static dir
loadable = openerpweb.addons_manifest.keys() loadable = openerpweb.addons_manifest.keys()
modules = {} modules = {}
@ -224,14 +226,14 @@ def module_installed_bypass_session(dbname):
sorted_modules = module_topological_sort(modules) sorted_modules = module_topological_sort(modules)
return sorted_modules return sorted_modules
def module_boot(req, db=None): def module_boot(db=None):
server_wide_modules = openerp.conf.server_wide_modules or ['web'] server_wide_modules = openerp.conf.server_wide_modules or ['web']
serverside = [] serverside = []
dbside = [] dbside = []
for i in server_wide_modules: for i in server_wide_modules:
if i in openerpweb.addons_manifest: if i in openerpweb.addons_manifest:
serverside.append(i) serverside.append(i)
monodb = db or db_monodb(req) monodb = db or db_monodb()
if monodb: if monodb:
dbside = module_installed_bypass_session(monodb) dbside = module_installed_bypass_session(monodb)
dbside = [i for i in dbside if i not in serverside] dbside = [i for i in dbside if i not in serverside]
@ -308,9 +310,9 @@ def fs2web(path):
"""convert FS path into web path""" """convert FS path into web path"""
return '/'.join(path.split(os.path.sep)) return '/'.join(path.split(os.path.sep))
def manifest_glob(req, extension, addons=None, db=None): def manifest_glob(extension, addons=None, db=None):
if addons is None: if addons is None:
addons = module_boot(req, db=db) addons = module_boot(db=db)
else: else:
addons = addons.split(',') addons = addons.split(',')
r = [] r = []
@ -326,7 +328,7 @@ def manifest_glob(req, extension, addons=None, db=None):
r.append((path, fs2web(path[len(addons_path):]))) r.append((path, fs2web(path[len(addons_path):])))
return r return r
def manifest_list(req, extension, mods=None, db=None): def manifest_list(extension, mods=None, db=None):
""" list ressources to load specifying either: """ list ressources to load specifying either:
mods: a comma separated string listing modules mods: a comma separated string listing modules
db: a database name (return all installed modules in that database) db: a database name (return all installed modules in that database)
@ -338,7 +340,7 @@ def manifest_list(req, extension, mods=None, db=None):
elif db: elif db:
path += '?' + urllib.urlencode({'db': db}) path += '?' + urllib.urlencode({'db': db})
return [path] return [path]
files = manifest_glob(req, extension, addons=mods, db=db) files = manifest_glob(extension, addons=mods, db=db)
return [wp for _fp, wp in files] return [wp for _fp, wp in files]
def get_last_modified(files): def get_last_modified(files):
@ -355,15 +357,13 @@ def get_last_modified(files):
for f in files) for f in files)
return datetime.datetime(1970, 1, 1) return datetime.datetime(1970, 1, 1)
def make_conditional(req, response, last_modified=None, etag=None): def make_conditional(response, last_modified=None, etag=None):
""" Makes the provided response conditional based upon the request, """ Makes the provided response conditional based upon the request,
and mandates revalidation from clients and mandates revalidation from clients
Uses Werkzeug's own :meth:`ETagResponseMixin.make_conditional`, after Uses Werkzeug's own :meth:`ETagResponseMixin.make_conditional`, after
setting ``last_modified`` and ``etag`` correctly on the response object setting ``last_modified`` and ``etag`` correctly on the response object
:param req: OpenERP request
:type req: web.common.http.WebRequest
:param response: Werkzeug response :param response: Werkzeug response
:type response: werkzeug.wrappers.Response :type response: werkzeug.wrappers.Response
:param datetime.datetime last_modified: last modification date of the response content :param datetime.datetime last_modified: last modification date of the response content
@ -379,7 +379,7 @@ def make_conditional(req, response, last_modified=None, etag=None):
response.set_etag(etag) response.set_etag(etag)
return response.make_conditional(req.httprequest) return response.make_conditional(req.httprequest)
def login_and_redirect(req, db, login, key, redirect_url='/'): def login_and_redirect(db, login, key, redirect_url='/'):
wsgienv = req.httprequest.environ wsgienv = req.httprequest.environ
env = dict( env = dict(
base_location=req.httprequest.url_root.rstrip('/'), base_location=req.httprequest.url_root.rstrip('/'),
@ -387,23 +387,23 @@ def login_and_redirect(req, db, login, key, redirect_url='/'):
REMOTE_ADDR=wsgienv['REMOTE_ADDR'], REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
) )
req.session.authenticate(db, login, key, env) req.session.authenticate(db, login, key, env)
return set_cookie_and_redirect(req, redirect_url) return set_cookie_and_redirect(redirect_url)
def set_cookie_and_redirect(req, redirect_url): def set_cookie_and_redirect(redirect_url):
redirect = werkzeug.utils.redirect(redirect_url, 303) redirect = werkzeug.utils.redirect(redirect_url, 303)
redirect.autocorrect_location_header = False redirect.autocorrect_location_header = False
cookie_val = urllib2.quote(simplejson.dumps(req.session_id)) cookie_val = urllib2.quote(simplejson.dumps(req.session_id))
redirect.set_cookie('instance0|session_id', cookie_val) redirect.set_cookie('instance0|session_id', cookie_val)
return redirect return redirect
def load_actions_from_ir_values(req, key, key2, models, meta): def load_actions_from_ir_values(key, key2, models, meta):
Values = req.session.model('ir.values') Values = req.session.model('ir.values')
actions = Values.get(key, key2, models, meta, req.context) actions = Values.get(key, key2, models, meta, req.context)
return [(id, name, clean_action(req, action)) return [(id, name, clean_action(action))
for id, name, action in actions] for id, name, action in actions]
def clean_action(req, action): def clean_action(action):
action.setdefault('flags', {}) action.setdefault('flags', {})
action_type = action.setdefault('type', 'ir.actions.act_window_close') action_type = action.setdefault('type', 'ir.actions.act_window_close')
if action_type == 'ir.actions.act_window': if action_type == 'ir.actions.act_window':
@ -521,7 +521,7 @@ def xml2json_from_elementtree(el, preserve_whitespaces=False):
res["children"] = kids res["children"] = kids
return res return res
def content_disposition(filename, req): def content_disposition(filename):
filename = filename.encode('utf8') filename = filename.encode('utf8')
escaped = urllib2.quote(filename) escaped = urllib2.quote(filename)
browser = req.httprequest.user_agent.browser browser = req.httprequest.user_agent.browser
@ -568,28 +568,28 @@ class Home(openerpweb.Controller):
_cp_path = '/' _cp_path = '/'
@openerpweb.httprequest @openerpweb.httprequest
def index(self, req, s_action=None, db=None, **kw): def index(self, s_action=None, db=None, **kw):
db, redir = db_monodb_redirect(req) db, redir = db_monodb_redirect()
if redir: if redir:
return redirect_with_hash(req, redir) return redirect_with_hash(redir)
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list(req, 'js', db=db)) js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list('js', db=db))
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list(req, 'css', db=db)) css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list('css', db=db))
r = html_template % { r = html_template % {
'js': js, 'js': js,
'css': css, 'css': css,
'modules': simplejson.dumps(module_boot(req, db=db)), 'modules': simplejson.dumps(module_boot(db=db)),
'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));' 'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));'
} }
return r return r
@openerpweb.httprequest @openerpweb.httprequest
def login(self, req, db, login, key): def login(self, db, login, key):
return login_and_redirect(req, db, login, key) return login_and_redirect(db, login, key)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def jsonrpc(self, req, service, method, args): def jsonrpc(self, service, method, args):
""" Method used by client APIs to contact OpenERP. """ """ Method used by client APIs to contact OpenERP. """
return getattr(req.session.proxy(service), method)(*args) return getattr(req.session.proxy(service), method)(*args)
@ -597,20 +597,20 @@ class WebClient(openerpweb.Controller):
_cp_path = "/web/webclient" _cp_path = "/web/webclient"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def csslist(self, req, mods=None): def csslist(self, mods=None):
return manifest_list(req, 'css', mods=mods) return manifest_list('css', mods=mods)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def jslist(self, req, mods=None): def jslist(self, mods=None):
return manifest_list(req, 'js', mods=mods) return manifest_list('js', mods=mods)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def qweblist(self, req, mods=None): def qweblist(self, mods=None):
return manifest_list(req, 'qweb', mods=mods) return manifest_list('qweb', mods=mods)
@openerpweb.httprequest @openerpweb.httprequest
def css(self, req, mods=None, db=None): def css(self, mods=None, db=None):
files = list(manifest_glob(req, 'css', addons=mods, db=db)) files = list(manifest_glob('css', addons=mods, db=db))
last_modified = get_last_modified(f[0] for f in files) last_modified = get_last_modified(f[0] for f in files)
if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified: if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
return werkzeug.wrappers.Response(status=304) return werkzeug.wrappers.Response(status=304)
@ -656,12 +656,12 @@ class WebClient(openerpweb.Controller):
content = '\n'.join(matches) content = '\n'.join(matches)
return make_conditional( return make_conditional(
req, req.make_response(content, [('Content-Type', 'text/css')]), req.make_response(content, [('Content-Type', 'text/css')]),
last_modified, checksum) last_modified, checksum)
@openerpweb.httprequest @openerpweb.httprequest
def js(self, req, mods=None, db=None): def js(self, mods=None, db=None):
files = [f[0] for f in manifest_glob(req, 'js', addons=mods, db=db)] files = [f[0] for f in manifest_glob('js', addons=mods, db=db)]
last_modified = get_last_modified(files) last_modified = get_last_modified(files)
if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified: if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
return werkzeug.wrappers.Response(status=304) return werkzeug.wrappers.Response(status=304)
@ -669,12 +669,12 @@ class WebClient(openerpweb.Controller):
content, checksum = concat_js(files) content, checksum = concat_js(files)
return make_conditional( return make_conditional(
req, req.make_response(content, [('Content-Type', 'application/javascript')]), req.make_response(content, [('Content-Type', 'application/javascript')]),
last_modified, checksum) last_modified, checksum)
@openerpweb.httprequest @openerpweb.httprequest
def qweb(self, req, mods=None, db=None): def qweb(self, mods=None, db=None):
files = [f[0] for f in manifest_glob(req, 'qweb', addons=mods, db=db)] files = [f[0] for f in manifest_glob('qweb', addons=mods, db=db)]
last_modified = get_last_modified(files) last_modified = get_last_modified(files)
if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified: if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
return werkzeug.wrappers.Response(status=304) return werkzeug.wrappers.Response(status=304)
@ -682,11 +682,11 @@ class WebClient(openerpweb.Controller):
content, checksum = concat_xml(files) content, checksum = concat_xml(files)
return make_conditional( return make_conditional(
req, req.make_response(content, [('Content-Type', 'text/xml')]), req.make_response(content, [('Content-Type', 'text/xml')]),
last_modified, checksum) last_modified, checksum)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def bootstrap_translations(self, req, mods): def bootstrap_translations(self, mods):
""" Load local translations from *.po files, as a temporary solution """ Load local translations from *.po files, as a temporary solution
until we have established a valid session. This is meant only until we have established a valid session. This is meant only
for translating the login page and db management chrome, using for translating the login page and db management chrome, using
@ -709,7 +709,7 @@ class WebClient(openerpweb.Controller):
"lang_parameters": None} "lang_parameters": None}
@openerpweb.jsonrequest @openerpweb.jsonrequest
def translations(self, req, mods, lang): def translations(self, mods, lang):
res_lang = req.session.model('res.lang') res_lang = req.session.model('res.lang')
ids = res_lang.search([("code", "=", lang)]) ids = res_lang.search([("code", "=", lang)])
lang_params = None lang_params = None
@ -734,20 +734,19 @@ class WebClient(openerpweb.Controller):
"lang_parameters": lang_params} "lang_parameters": lang_params}
@openerpweb.jsonrequest @openerpweb.jsonrequest
def version_info(self, req): def version_info(self):
return openerp.service.common.exp_version() return openerp.service.common.exp_version()
class Proxy(openerpweb.Controller): class Proxy(openerpweb.Controller):
_cp_path = '/web/proxy' _cp_path = '/web/proxy'
@openerpweb.jsonrequest @openerpweb.jsonrequest
def load(self, req, path): def load(self, path):
""" Proxies an HTTP request through a JSON request. """ Proxies an HTTP request through a JSON request.
It is strongly recommended to not request binary files through this, It is strongly recommended to not request binary files through this,
as the result will be a binary data blob as well. as the result will be a binary data blob as well.
:param req: OpenERP request
:param path: actual request path :param path: actual request path
:return: file content :return: file content
""" """
@ -760,11 +759,11 @@ class Database(openerpweb.Controller):
_cp_path = "/web/database" _cp_path = "/web/database"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_list(self, req): def get_list(self):
return db_list(req) return db_list()
@openerpweb.jsonrequest @openerpweb.jsonrequest
def create(self, req, fields): def create(self, fields):
params = dict(map(operator.itemgetter('name', 'value'), fields)) params = dict(map(operator.itemgetter('name', 'value'), fields))
return req.session.proxy("db").create_database( return req.session.proxy("db").create_database(
params['super_admin_pwd'], params['super_admin_pwd'],
@ -774,7 +773,7 @@ class Database(openerpweb.Controller):
params['create_admin_pwd']) params['create_admin_pwd'])
@openerpweb.jsonrequest @openerpweb.jsonrequest
def duplicate(self, req, fields): def duplicate(self, fields):
params = dict(map(operator.itemgetter('name', 'value'), fields)) params = dict(map(operator.itemgetter('name', 'value'), fields))
return req.session.proxy("db").duplicate_database( return req.session.proxy("db").duplicate_database(
params['super_admin_pwd'], params['super_admin_pwd'],
@ -782,7 +781,7 @@ class Database(openerpweb.Controller):
params['db_name']) params['db_name'])
@openerpweb.jsonrequest @openerpweb.jsonrequest
def duplicate(self, req, fields): def duplicate(self, fields):
params = dict(map(operator.itemgetter('name', 'value'), fields)) params = dict(map(operator.itemgetter('name', 'value'), fields))
duplicate_attrs = ( duplicate_attrs = (
params['super_admin_pwd'], params['super_admin_pwd'],
@ -793,7 +792,7 @@ class Database(openerpweb.Controller):
return req.session.proxy("db").duplicate_database(*duplicate_attrs) return req.session.proxy("db").duplicate_database(*duplicate_attrs)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def drop(self, req, fields): def drop(self, fields):
password, db = operator.itemgetter( password, db = operator.itemgetter(
'drop_pwd', 'drop_db')( 'drop_pwd', 'drop_db')(
dict(map(operator.itemgetter('name', 'value'), fields))) dict(map(operator.itemgetter('name', 'value'), fields)))
@ -806,7 +805,7 @@ class Database(openerpweb.Controller):
return {'error': _('Could not drop database !'), 'title': _('Drop Database')} return {'error': _('Could not drop database !'), 'title': _('Drop Database')}
@openerpweb.httprequest @openerpweb.httprequest
def backup(self, req, backup_db, backup_pwd, token): def backup(self, backup_db, backup_pwd, token):
try: try:
db_dump = base64.b64decode( db_dump = base64.b64decode(
req.session.proxy("db").dump(backup_pwd, backup_db)) req.session.proxy("db").dump(backup_pwd, backup_db))
@ -817,14 +816,14 @@ class Database(openerpweb.Controller):
} }
return req.make_response(db_dump, return req.make_response(db_dump,
[('Content-Type', 'application/octet-stream; charset=binary'), [('Content-Type', 'application/octet-stream; charset=binary'),
('Content-Disposition', content_disposition(filename, req))], ('Content-Disposition', content_disposition(filename))],
{'fileToken': int(token)} {'fileToken': int(token)}
) )
except Exception, e: except Exception, e:
return simplejson.dumps([[],[{'error': openerp.tools.ustr(e), 'title': _('Backup Database')}]]) return simplejson.dumps([[],[{'error': openerp.tools.ustr(e), 'title': _('Backup Database')}]])
@openerpweb.httprequest @openerpweb.httprequest
def restore(self, req, db_file, restore_pwd, new_db): def restore(self, db_file, restore_pwd, new_db):
try: try:
data = base64.b64encode(db_file.read()) data = base64.b64encode(db_file.read())
req.session.proxy("db").restore(restore_pwd, new_db, data) req.session.proxy("db").restore(restore_pwd, new_db, data)
@ -833,7 +832,7 @@ class Database(openerpweb.Controller):
raise Exception("AccessDenied") raise Exception("AccessDenied")
@openerpweb.jsonrequest @openerpweb.jsonrequest
def change_password(self, req, fields): def change_password(self, fields):
old_password, new_password = operator.itemgetter( old_password, new_password = operator.itemgetter(
'old_pwd', 'new_pwd')( 'old_pwd', 'new_pwd')(
dict(map(operator.itemgetter('name', 'value'), fields))) dict(map(operator.itemgetter('name', 'value'), fields)))
@ -847,7 +846,7 @@ class Database(openerpweb.Controller):
class Session(openerpweb.Controller): class Session(openerpweb.Controller):
_cp_path = "/web/session" _cp_path = "/web/session"
def session_info(self, req): def session_info(self):
req.session.ensure_valid() req.session.ensure_valid()
return { return {
"session_id": req.session_id, "session_id": req.session_id,
@ -858,11 +857,11 @@ class Session(openerpweb.Controller):
} }
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_session_info(self, req): def get_session_info(self):
return self.session_info(req) return self.session_info()
@openerpweb.jsonrequest @openerpweb.jsonrequest
def authenticate(self, req, db, login, password, base_location=None): def authenticate(self, db, login, password, base_location=None):
wsgienv = req.httprequest.environ wsgienv = req.httprequest.environ
env = dict( env = dict(
base_location=base_location, base_location=base_location,
@ -871,10 +870,10 @@ class Session(openerpweb.Controller):
) )
req.session.authenticate(db, login, password, env) req.session.authenticate(db, login, password, env)
return self.session_info(req) return self.session_info()
@openerpweb.jsonrequest @openerpweb.jsonrequest
def change_password (self,req,fields): def change_password (self, fields):
old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')( old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')(
dict(map(operator.itemgetter('name', 'value'), fields))) dict(map(operator.itemgetter('name', 'value'), fields)))
if not (old_password.strip() and new_password.strip() and confirm_password.strip()): if not (old_password.strip() and new_password.strip() and confirm_password.strip()):
@ -890,24 +889,24 @@ class Session(openerpweb.Controller):
return {'error': _('Error, password not changed !'), 'title': _('Change Password')} return {'error': _('Error, password not changed !'), 'title': _('Change Password')}
@openerpweb.jsonrequest @openerpweb.jsonrequest
def sc_list(self, req): def sc_list(self):
return req.session.model('ir.ui.view_sc').get_sc( return req.session.model('ir.ui.view_sc').get_sc(
req.session._uid, "ir.ui.menu", req.context) req.session._uid, "ir.ui.menu", req.context)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_lang_list(self, req): def get_lang_list(self):
try: try:
return req.session.proxy("db").list_lang() or [] return req.session.proxy("db").list_lang() or []
except Exception, e: except Exception, e:
return {"error": e, "title": _("Languages")} return {"error": e, "title": _("Languages")}
@openerpweb.jsonrequest @openerpweb.jsonrequest
def modules(self, req): def modules(self):
# return all installed modules. Web client is smart enough to not load a module twice # return all installed modules. Web client is smart enough to not load a module twice
return module_installed(req) return module_installed()
@openerpweb.jsonrequest @openerpweb.jsonrequest
def save_session_action(self, req, the_action): def save_session_action(self, the_action):
""" """
This method store an action object in the session object and returns an integer This method store an action object in the session object and returns an integer
identifying that action. The method get_session_action() can be used to get identifying that action. The method get_session_action() can be used to get
@ -931,7 +930,7 @@ class Session(openerpweb.Controller):
return key return key
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_session_action(self, req, key): def get_session_action(self, key):
""" """
Gets back a previously saved action. This method can return None if the action Gets back a previously saved action. This method can return None if the action
was saved since too much time (this case should be handled in a smart way). was saved since too much time (this case should be handled in a smart way).
@ -947,23 +946,21 @@ class Session(openerpweb.Controller):
return saved_actions["actions"].get(key) return saved_actions["actions"].get(key)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def check(self, req): def check(self):
req.session.assert_valid() req.session.assert_valid()
return None return None
@openerpweb.jsonrequest @openerpweb.jsonrequest
def destroy(self, req): def destroy(self):
req.session._suicide = True req.session._suicide = True
class Menu(openerpweb.Controller): class Menu(openerpweb.Controller):
_cp_path = "/web/menu" _cp_path = "/web/menu"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_user_roots(self, req): def get_user_roots(self):
""" Return all root menu ids visible for the session user. """ Return all root menu ids visible for the session user.
:param req: A request object, with an OpenERP session attribute
:type req: < session -> OpenERPSession >
:return: the root menu ids :return: the root menu ids
:rtype: list(int) :rtype: list(int)
""" """
@ -983,18 +980,16 @@ class Menu(openerpweb.Controller):
return Menus.search(menu_domain, 0, False, False, req.context) return Menus.search(menu_domain, 0, False, False, req.context)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def load(self, req): def load(self):
""" Loads all menu items (all applications and their sub-menus). """ Loads all menu items (all applications and their sub-menus).
:param req: A request object, with an OpenERP session attribute
:type req: < session -> OpenERPSession >
:return: the menu root :return: the menu root
:rtype: dict('children': menu_nodes) :rtype: dict('children': menu_nodes)
""" """
Menus = req.session.model('ir.ui.menu') Menus = req.session.model('ir.ui.menu')
fields = ['name', 'sequence', 'parent_id', 'action'] fields = ['name', 'sequence', 'parent_id', 'action']
menu_root_ids = self.get_user_roots(req) menu_root_ids = self.get_user_roots()
menu_roots = Menus.read(menu_root_ids, fields, req.context) if menu_root_ids else [] menu_roots = Menus.read(menu_root_ids, fields, req.context) if menu_root_ids else []
menu_root = { menu_root = {
'id': False, 'id': False,
@ -1036,7 +1031,7 @@ class Menu(openerpweb.Controller):
return menu_root return menu_root
@openerpweb.jsonrequest @openerpweb.jsonrequest
def load_needaction(self, req, menu_ids): def load_needaction(self, menu_ids):
""" Loads needaction counters for specific menu ids. """ Loads needaction counters for specific menu ids.
:return: needaction data :return: needaction data
@ -1045,9 +1040,9 @@ class Menu(openerpweb.Controller):
return req.session.model('ir.ui.menu').get_needaction_data(menu_ids, req.context) return req.session.model('ir.ui.menu').get_needaction_data(menu_ids, req.context)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def action(self, req, menu_id): def action(self, menu_id):
# still used by web_shortcut # still used by web_shortcut
actions = load_actions_from_ir_values(req,'action', 'tree_but_open', actions = load_actions_from_ir_values('action', 'tree_but_open',
[('ir.ui.menu', menu_id)], False) [('ir.ui.menu', menu_id)], False)
return {"action": actions} return {"action": actions}
@ -1055,15 +1050,13 @@ class DataSet(openerpweb.Controller):
_cp_path = "/web/dataset" _cp_path = "/web/dataset"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def search_read(self, req, model, fields=False, offset=0, limit=False, domain=None, sort=None): def search_read(self, model, fields=False, offset=0, limit=False, domain=None, sort=None):
return self.do_search_read(req, model, fields, offset, limit, domain, sort) return self.do_search_read(model, fields, offset, limit, domain, sort)
def do_search_read(self, req, model, fields=False, offset=0, limit=False, domain=None def do_search_read(self, model, fields=False, offset=0, limit=False, domain=None
, sort=None): , sort=None):
""" Performs a search() followed by a read() (if needed) using the """ Performs a search() followed by a read() (if needed) using the
provided search criteria provided search criteria
:param req: a JSON-RPC request object
:type req: openerpweb.JsonRequest
:param str model: the name of the model to search on :param str model: the name of the model to search on
:param fields: a list of the fields to return in the result records :param fields: a list of the fields to return in the result records
:type fields: [str] :type fields: [str]
@ -1099,7 +1092,7 @@ class DataSet(openerpweb.Controller):
} }
@openerpweb.jsonrequest @openerpweb.jsonrequest
def load(self, req, model, id, fields): def load(self, model, id, fields):
m = req.session.model(model) m = req.session.model(model)
value = {} value = {}
r = m.read([id], False, req.context) r = m.read([id], False, req.context)
@ -1107,10 +1100,10 @@ class DataSet(openerpweb.Controller):
value = r[0] value = r[0]
return {'value': value} return {'value': value}
def call_common(self, req, model, method, args, domain_id=None, context_id=None): def call_common(self, model, method, args, domain_id=None, context_id=None):
return self._call_kw(req, model, method, args, {}) return self._call_kw(model, method, args, {})
def _call_kw(self, req, model, method, args, kwargs): def _call_kw(self, model, method, args, kwargs):
# Temporary implements future display_name special field for model#read() # Temporary implements future display_name special field for model#read()
if method == 'read' and kwargs.get('context', {}).get('future_display_name'): if method == 'read' and kwargs.get('context', {}).get('future_display_name'):
if 'display_name' in args[1]: if 'display_name' in args[1]:
@ -1125,26 +1118,26 @@ class DataSet(openerpweb.Controller):
return getattr(req.session.model(model), method)(*args, **kwargs) return getattr(req.session.model(model), method)(*args, **kwargs)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def call(self, req, model, method, args, domain_id=None, context_id=None): def call(self, model, method, args, domain_id=None, context_id=None):
return self._call_kw(req, model, method, args, {}) return self._call_kw(model, method, args, {})
@openerpweb.jsonrequest @openerpweb.jsonrequest
def call_kw(self, req, model, method, args, kwargs): def call_kw(self, model, method, args, kwargs):
return self._call_kw(req, model, method, args, kwargs) return self._call_kw(model, method, args, kwargs)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def call_button(self, req, model, method, args, domain_id=None, context_id=None): def call_button(self, model, method, args, domain_id=None, context_id=None):
action = self._call_kw(req, model, method, args, {}) action = self._call_kw(model, method, args, {})
if isinstance(action, dict) and action.get('type') != '': if isinstance(action, dict) and action.get('type') != '':
return clean_action(req, action) return clean_action(action)
return False return False
@openerpweb.jsonrequest @openerpweb.jsonrequest
def exec_workflow(self, req, model, id, signal): def exec_workflow(self, model, id, signal):
return req.session.exec_workflow(model, id, signal) return req.session.exec_workflow(model, id, signal)
@openerpweb.jsonrequest @openerpweb.jsonrequest
def resequence(self, req, model, ids, field='sequence', offset=0): def resequence(self, model, ids, field='sequence', offset=0):
""" Re-sequences a number of records in the model, by their ids """ Re-sequences a number of records in the model, by their ids
The re-sequencing starts at the first model of ``ids``, the sequence The re-sequencing starts at the first model of ``ids``, the sequence
@ -1170,7 +1163,7 @@ class View(openerpweb.Controller):
_cp_path = "/web/view" _cp_path = "/web/view"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def add_custom(self, req, view_id, arch): def add_custom(self, view_id, arch):
CustomView = req.session.model('ir.ui.view.custom') CustomView = req.session.model('ir.ui.view.custom')
CustomView.create({ CustomView.create({
'user_id': req.session._uid, 'user_id': req.session._uid,
@ -1180,7 +1173,7 @@ class View(openerpweb.Controller):
return {'result': True} return {'result': True}
@openerpweb.jsonrequest @openerpweb.jsonrequest
def undo_custom(self, req, view_id, reset=False): def undo_custom(self, view_id, reset=False):
CustomView = req.session.model('ir.ui.view.custom') CustomView = req.session.model('ir.ui.view.custom')
vcustom = CustomView.search([('user_id', '=', req.session._uid), ('ref_id' ,'=', view_id)], vcustom = CustomView.search([('user_id', '=', req.session._uid), ('ref_id' ,'=', view_id)],
0, False, False, req.context) 0, False, False, req.context)
@ -1196,16 +1189,16 @@ class TreeView(View):
_cp_path = "/web/treeview" _cp_path = "/web/treeview"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def action(self, req, model, id): def action(self, model, id):
return load_actions_from_ir_values( return load_actions_from_ir_values(
req,'action', 'tree_but_open',[(model, id)], 'action', 'tree_but_open',[(model, id)],
False) False)
class Binary(openerpweb.Controller): class Binary(openerpweb.Controller):
_cp_path = "/web/binary" _cp_path = "/web/binary"
@openerpweb.httprequest @openerpweb.httprequest
def image(self, req, model, id, field, **kw): def image(self, model, id, field, **kw):
last_update = '__last_update' last_update = '__last_update'
Model = req.session.model(model) Model = req.session.model(model)
headers = [('Content-Type', 'image/png')] headers = [('Content-Type', 'image/png')]
@ -1245,7 +1238,7 @@ class Binary(openerpweb.Controller):
image_data = base64.b64decode(image_base64) image_data = base64.b64decode(image_base64)
except Exception: except Exception:
image_data = self.placeholder(req) image_data = self.placeholder()
headers.append(('ETag', retag)) headers.append(('ETag', retag))
headers.append(('Content-Length', len(image_data))) headers.append(('Content-Length', len(image_data)))
try: try:
@ -1255,20 +1248,18 @@ class Binary(openerpweb.Controller):
pass pass
return req.make_response(image_data, headers) return req.make_response(image_data, headers)
def placeholder(self, req, image='placeholder.png'): def placeholder(self, image='placeholder.png'):
addons_path = openerpweb.addons_manifest['web']['addons_path'] addons_path = openerpweb.addons_manifest['web']['addons_path']
return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', image), 'rb').read() return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', image), 'rb').read()
@openerpweb.httprequest @openerpweb.httprequest
def saveas(self, req, model, field, id=None, filename_field=None, **kw): def saveas(self, model, field, id=None, filename_field=None, **kw):
""" Download link for files stored as binary fields. """ Download link for files stored as binary fields.
If the ``id`` parameter is omitted, fetches the default value for the If the ``id`` parameter is omitted, fetches the default value for the
binary field (via ``default_get``), otherwise fetches the field for binary field (via ``default_get``), otherwise fetches the field for
that precise record. that precise record.
:param req: OpenERP request
:type req: :class:`web.common.http.HttpRequest`
:param str model: name of the model to fetch the binary from :param str model: name of the model to fetch the binary from
:param str field: binary field :param str field: binary field
:param str id: id of the record from which to fetch the binary :param str id: id of the record from which to fetch the binary
@ -1292,10 +1283,10 @@ class Binary(openerpweb.Controller):
filename = res.get(filename_field, '') or filename filename = res.get(filename_field, '') or filename
return req.make_response(filecontent, return req.make_response(filecontent,
[('Content-Type', 'application/octet-stream'), [('Content-Type', 'application/octet-stream'),
('Content-Disposition', content_disposition(filename, req))]) ('Content-Disposition', content_disposition(filename))])
@openerpweb.httprequest @openerpweb.httprequest
def saveas_ajax(self, req, data, token): def saveas_ajax(self, data, token):
jdata = simplejson.loads(data) jdata = simplejson.loads(data)
model = jdata['model'] model = jdata['model']
field = jdata['field'] field = jdata['field']
@ -1324,11 +1315,11 @@ class Binary(openerpweb.Controller):
filename = res.get(filename_field, '') or filename filename = res.get(filename_field, '') or filename
return req.make_response(filecontent, return req.make_response(filecontent,
headers=[('Content-Type', 'application/octet-stream'), headers=[('Content-Type', 'application/octet-stream'),
('Content-Disposition', content_disposition(filename, req))], ('Content-Disposition', content_disposition(filename))],
cookies={'fileToken': int(token)}) cookies={'fileToken': int(token)})
@openerpweb.httprequest @openerpweb.httprequest
def upload(self, req, callback, ufile): def upload(self, callback, ufile):
# TODO: might be useful to have a configuration flag for max-length file uploads # TODO: might be useful to have a configuration flag for max-length file uploads
out = """<script language="javascript" type="text/javascript"> out = """<script language="javascript" type="text/javascript">
var win = window.top.window; var win = window.top.window;
@ -1343,7 +1334,7 @@ class Binary(openerpweb.Controller):
return out % (simplejson.dumps(callback), simplejson.dumps(args)) return out % (simplejson.dumps(callback), simplejson.dumps(args))
@openerpweb.httprequest @openerpweb.httprequest
def upload_attachment(self, req, callback, model, id, ufile): def upload_attachment(self, callback, model, id, ufile):
Model = req.session.model('ir.attachment') Model = req.session.model('ir.attachment')
out = """<script language="javascript" type="text/javascript"> out = """<script language="javascript" type="text/javascript">
var win = window.top.window; var win = window.top.window;
@ -1366,20 +1357,20 @@ class Binary(openerpweb.Controller):
return out % (simplejson.dumps(callback), simplejson.dumps(args)) return out % (simplejson.dumps(callback), simplejson.dumps(args))
@openerpweb.httprequest @openerpweb.httprequest
def company_logo(self, req, dbname=None): def company_logo(self, dbname=None):
# TODO add etag, refactor to use /image code for etag # TODO add etag, refactor to use /image code for etag
uid = None uid = None
if req.session._db: if req.session._db:
dbname = req.session._db dbname = req.session._db
uid = req.session._uid uid = req.session._uid
elif dbname is None: elif dbname is None:
dbname = db_monodb(req) dbname = db_monodb()
if not uid: if not uid:
uid = openerp.SUPERUSER_ID uid = openerp.SUPERUSER_ID
if not dbname: if not dbname:
image_data = self.placeholder(req, 'logo.png') image_data = self.placeholder('logo.png')
else: else:
try: try:
# create an empty registry # create an empty registry
@ -1395,9 +1386,9 @@ class Binary(openerpweb.Controller):
if row and row[0]: if row and row[0]:
image_data = str(row[0]).decode('base64') image_data = str(row[0]).decode('base64')
else: else:
image_data = self.placeholder(req, 'nologo.png') image_data = self.placeholder('nologo.png')
except Exception: except Exception:
image_data = self.placeholder(req, 'logo.png') image_data = self.placeholder('logo.png')
headers = [ headers = [
('Content-Type', 'image/png'), ('Content-Type', 'image/png'),
@ -1409,7 +1400,7 @@ class Action(openerpweb.Controller):
_cp_path = "/web/action" _cp_path = "/web/action"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def load(self, req, action_id, do_not_eval=False): def load(self, action_id, do_not_eval=False):
Actions = req.session.model('ir.actions.actions') Actions = req.session.model('ir.actions.actions')
value = False value = False
try: try:
@ -1431,15 +1422,15 @@ class Action(openerpweb.Controller):
ctx.update(req.context) ctx.update(req.context)
action = req.session.model(action_type).read([action_id], False, ctx) action = req.session.model(action_type).read([action_id], False, ctx)
if action: if action:
value = clean_action(req, action[0]) value = clean_action(action[0])
return value return value
@openerpweb.jsonrequest @openerpweb.jsonrequest
def run(self, req, action_id): def run(self, action_id):
return_action = req.session.model('ir.actions.server').run( return_action = req.session.model('ir.actions.server').run(
[action_id], req.context) [action_id], req.context)
if return_action: if return_action:
return clean_action(req, return_action) return clean_action(return_action)
else: else:
return False return False
@ -1447,7 +1438,7 @@ class Export(openerpweb.Controller):
_cp_path = "/web/export" _cp_path = "/web/export"
@openerpweb.jsonrequest @openerpweb.jsonrequest
def formats(self, req): def formats(self):
""" Returns all valid export formats """ Returns all valid export formats
:returns: for each export format, a pair of identifier and printable name :returns: for each export format, a pair of identifier and printable name
@ -1460,20 +1451,20 @@ class Export(openerpweb.Controller):
if hasattr(controller, 'fmt') if hasattr(controller, 'fmt')
], key=operator.itemgetter("label")) ], key=operator.itemgetter("label"))
def fields_get(self, req, model): def fields_get(self, model):
Model = req.session.model(model) Model = req.session.model(model)
fields = Model.fields_get(False, req.context) fields = Model.fields_get(False, req.context)
return fields return fields
@openerpweb.jsonrequest @openerpweb.jsonrequest
def get_fields(self, req, model, prefix='', parent_name= '', def get_fields(self, model, prefix='', parent_name= '',
import_compat=True, parent_field_type=None, import_compat=True, parent_field_type=None,
exclude=None): exclude=None):
if import_compat and parent_field_type == "many2one": if import_compat and parent_field_type == "many2one":
fields = {} fields = {}
else: else:
fields = self.fields_get(req, model) fields = self.fields_get(model)
if import_compat: if import_compat:
fields.pop('id', None) fields.pop('id', None)
@ -1515,23 +1506,23 @@ class Export(openerpweb.Controller):
return records return records
@openerpweb.jsonrequest @openerpweb.jsonrequest
def namelist(self,req, model, export_id): def namelist(self, model, export_id):
# TODO: namelist really has no reason to be in Python (although itertools.groupby helps) # TODO: namelist really has no reason to be in Python (although itertools.groupby helps)
export = req.session.model("ir.exports").read([export_id])[0] export = req.session.model("ir.exports").read([export_id])[0]
export_fields_list = req.session.model("ir.exports.line").read( export_fields_list = req.session.model("ir.exports.line").read(
export['export_fields']) export['export_fields'])
fields_data = self.fields_info( fields_data = self.fields_info(
req, model, map(operator.itemgetter('name'), export_fields_list)) model, map(operator.itemgetter('name'), export_fields_list))
return [ return [
{'name': field['name'], 'label': fields_data[field['name']]} {'name': field['name'], 'label': fields_data[field['name']]}
for field in export_fields_list for field in export_fields_list
] ]
def fields_info(self, req, model, export_fields): def fields_info(self, model, export_fields):
info = {} info = {}
fields = self.fields_get(req, model) fields = self.fields_get(model)
if ".id" in export_fields: if ".id" in export_fields:
fields['.id'] = fields.pop('id', {'string': 'ID'}) fields['.id'] = fields.pop('id', {'string': 'ID'})
@ -1570,7 +1561,7 @@ class Export(openerpweb.Controller):
if length == 2: if length == 2:
# subfields is a seq of $base/*rest, and not loaded yet # subfields is a seq of $base/*rest, and not loaded yet
info.update(self.graft_subfields( info.update(self.graft_subfields(
req, fields[base]['relation'], base, fields[base]['string'], fields[base]['relation'], base, fields[base]['string'],
subfields subfields
)) ))
else: else:
@ -1578,11 +1569,11 @@ class Export(openerpweb.Controller):
return info return info
def graft_subfields(self, req, model, prefix, prefix_string, fields): def graft_subfields(self, model, prefix, prefix_string, fields):
export_fields = [field.split('/', 1)[1] for field in fields] export_fields = [field.split('/', 1)[1] for field in fields]
return ( return (
(prefix + '/' + k, prefix_string + '/' + v) (prefix + '/' + k, prefix_string + '/' + v)
for k, v in self.fields_info(req, model, export_fields).iteritems()) for k, v in self.fields_info(model, export_fields).iteritems())
class ExportFormat(object): class ExportFormat(object):
@property @property
@ -1608,7 +1599,7 @@ class ExportFormat(object):
raise NotImplementedError() raise NotImplementedError()
@openerpweb.httprequest @openerpweb.httprequest
def index(self, req, data, token): def index(self, data, token):
model, fields, ids, domain, import_compat = \ model, fields, ids, domain, import_compat = \
operator.itemgetter('model', 'fields', 'ids', 'domain', operator.itemgetter('model', 'fields', 'ids', 'domain',
'import_compat')( 'import_compat')(
@ -1628,7 +1619,7 @@ class ExportFormat(object):
return req.make_response(self.from_data(columns_headers, import_data), return req.make_response(self.from_data(columns_headers, import_data),
headers=[('Content-Disposition', headers=[('Content-Disposition',
content_disposition(self.filename(model), req)), content_disposition(self.filename(model))),
('Content-Type', self.content_type)], ('Content-Type', self.content_type)],
cookies={'fileToken': int(token)}) cookies={'fileToken': int(token)})
@ -1719,7 +1710,7 @@ class Reports(openerpweb.Controller):
} }
@openerpweb.httprequest @openerpweb.httprequest
def index(self, req, action, token): def index(self, action, token):
action = simplejson.loads(action) action = simplejson.loads(action)
report_srv = req.session.proxy("report") report_srv = req.session.proxy("report")
@ -1767,7 +1758,7 @@ class Reports(openerpweb.Controller):
return req.make_response(report, return req.make_response(report,
headers=[ headers=[
('Content-Disposition', content_disposition(file_name, req)), ('Content-Disposition', content_disposition(file_name)),
('Content-Type', report_mimetype), ('Content-Type', report_mimetype),
('Content-Length', len(report))], ('Content-Length', len(report))],
cookies={'fileToken': int(token)}) cookies={'fileToken': int(token)})

View File

@ -34,6 +34,9 @@ import openerp
import session import session
import inspect
import functools
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
#---------------------------------------------------------- #----------------------------------------------------------
@ -197,10 +200,10 @@ class JsonRequest(WebRequest):
else: else:
self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral) self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral)
self.init(self.jsonrequest.get("params", {})) self.init(self.jsonrequest.get("params", {}))
if _logger.isEnabledFor(logging.DEBUG): #if _logger.isEnabledFor(logging.DEBUG):
_logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest)) # _logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest))
response['id'] = self.jsonrequest.get('id') response['id'] = self.jsonrequest.get('id')
response["result"] = method(self, **self.params) response["result"] = method(**self.params)
except session.AuthenticationError, e: except session.AuthenticationError, e:
se = serialize_exception(e) se = serialize_exception(e)
error = { error = {
@ -292,9 +295,9 @@ class HttpRequest(WebRequest):
akw[key] = value akw[key] = value
else: else:
akw[key] = type(value) akw[key] = type(value)
_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw) #_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
try: try:
r = method(self, **self.params) r = method(**self.params)
except Exception, e: except Exception, e:
_logger.exception("An exception occured during an http request") _logger.exception("An exception occured during an http request")
se = serialize_exception(e) se = serialize_exception(e)
@ -349,6 +352,23 @@ def httprequest(f):
f.exposed = 'http' f.exposed = 'http'
return f return f
#----------------------------------------------------------
# Local storage of requests
#----------------------------------------------------------
from werkzeug.local import LocalStack
_request_stack = LocalStack()
def set_request(request):
class with_obj(object):
def __enter__(self):
_request_stack.push(request)
def __exit__(self, *args):
_request_stack.pop()
return with_obj()
request = _request_stack()
#---------------------------------------------------------- #----------------------------------------------------------
# Controller registration with a metaclass # Controller registration with a metaclass
#---------------------------------------------------------- #----------------------------------------------------------
@ -363,6 +383,19 @@ controllers_path = {}
class ControllerType(type): class ControllerType(type):
def __init__(cls, name, bases, attrs): def __init__(cls, name, bases, attrs):
super(ControllerType, cls).__init__(name, bases, attrs) super(ControllerType, cls).__init__(name, bases, attrs)
# create wrappers for old-style methods with req as first argument
cls._methods_wrapper = {}
for k, v in attrs.items():
if inspect.isfunction(v):
spec = inspect.getargspec(v)
first_arg = spec.args[1] if len(spec.args) >= 2 else None
if first_arg in ["req", "request"]:
def build_new(nv):
return lambda self, *args, **kwargs: nv(self, request, *args, **kwargs)
cls._methods_wrapper[k] = build_new(v)
# store the controller in the controllers list
name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls) name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
controllers_class.append(name_class) controllers_class.append(name_class)
path = attrs.get('_cp_path') path = attrs.get('_cp_path')
@ -380,6 +413,12 @@ class Controller(object):
return object.__new__(cls) return object.__new__(cls)
def get_wrapped_method(self, name):
if name in self.__class__._methods_wrapper:
return functools.partial(self.__class__._methods_wrapper[name], self)
else:
return getattr(self, name)
#---------------------------------------------------------- #----------------------------------------------------------
# Session context manager # Session context manager
#---------------------------------------------------------- #----------------------------------------------------------
@ -608,12 +647,21 @@ class Root(object):
method = getattr(c, method_name, None) method = getattr(c, method_name, None)
if method: if method:
exposed = getattr(method, 'exposed', False) exposed = getattr(method, 'exposed', False)
method = c.get_wrapped_method(method_name)
if exposed == 'json': if exposed == 'json':
_logger.debug("Dispatch json to %s %s %s", ps, c, method_name) _logger.debug("Dispatch json to %s %s %s", ps, c, method_name)
return lambda request: JsonRequest(request).dispatch(method) def fct(_request):
_req = JsonRequest(_request)
with set_request(_req):
return request.dispatch(method)
return fct
elif exposed == 'http': elif exposed == 'http':
_logger.debug("Dispatch http to %s %s %s", ps, c, method_name) _logger.debug("Dispatch http to %s %s %s", ps, c, method_name)
return lambda request: HttpRequest(request).dispatch(method) def fct(_request):
_req = HttpRequest(_request)
with set_request(_req):
return request.dispatch(method)
return fct
if method_name != "index": if method_name != "index":
method_name = "index" method_name = "index"
continue continue

View File

@ -2,30 +2,36 @@
import mock import mock
import unittest2 import unittest2
import openerp.addons.web.controllers.main import openerp.addons.web.controllers.main
from openerp.addons.web.http import request as req
from openerp.addons.web.http import set_request
class TestDataSetController(unittest2.TestCase): class TestDataSetController(unittest2.TestCase):
def setUp(self): def setUp(self):
self.dataset = openerp.addons.web.controllers.main.DataSet() self.dataset = openerp.addons.web.controllers.main.DataSet()
self.request = mock.Mock() self.tmp_req = set_request(mock.Mock())
self.read = self.request.session.model().read self.tmp_req.__enter__()
self.search = self.request.session.model().search self.read = req.session.model().read
self.search = req.session.model().search
def tearDown(self):
self.tmp_req.__exit__()
def test_empty_find(self): def test_empty_find(self):
self.search.return_value = [] self.search.return_value = []
self.read.return_value = [] self.read.return_value = []
self.assertEqual( self.assertEqual(
self.dataset.do_search_read(self.request, 'fake.model'), self.dataset.do_search_read('fake.model'),
{'records': [], 'length': 0}) {'records': [], 'length': 0})
self.read.assert_called_once_with( self.read.assert_called_once_with(
[], False, self.request.context) [], False, req.context)
def test_regular_find(self): def test_regular_find(self):
self.search.return_value = [1, 2, 3] self.search.return_value = [1, 2, 3]
self.dataset.do_search_read(self.request, 'fake.model') self.dataset.do_search_read('fake.model')
self.read.assert_called_once_with( self.read.assert_called_once_with(
[1, 2, 3], False,self.request.context) [1, 2, 3], False, req.context)
def test_ids_shortcut(self): def test_ids_shortcut(self):
self.search.return_value = [1, 2, 3] self.search.return_value = [1, 2, 3]
@ -36,6 +42,6 @@ class TestDataSetController(unittest2.TestCase):
] ]
self.assertEqual( self.assertEqual(
self.dataset.do_search_read(self.request, 'fake.model', ['id']), self.dataset.do_search_read('fake.model', ['id']),
{'records': [{'id': 1}, {'id': 2}, {'id': 3}], 'length': 3}) {'records': [{'id': 1}, {'id': 2}, {'id': 3}], 'length': 3})
self.assertFalse(self.read.called) self.assertFalse(self.read.called)

View File

@ -2,6 +2,8 @@
import collections import collections
import mock import mock
import unittest2 import unittest2
from openerp.addons.web.http import request as req
from openerp.addons.web.http import set_request
from ..controllers import main from ..controllers import main
@ -13,12 +15,13 @@ class Placeholder(object):
class LoadTest(unittest2.TestCase): class LoadTest(unittest2.TestCase):
def setUp(self): def setUp(self):
self.menu = main.Menu() self.menu = main.Menu()
self.request = mock.Mock() self.tmp_req = set_request(mock.Mock())
self.tmp_req.__enter__()
# Have self.request.session.model() return a different mock object for # Have self.request.session.model() return a different mock object for
# each model (but always the same mock for a given model name) # each model (but always the same mock for a given model name)
models = collections.defaultdict(mock.Mock) models = collections.defaultdict(mock.Mock)
model = self.request.session.model.side_effect = \ model = req.session.model.side_effect = \
lambda model_name: models[model_name] lambda model_name: models[model_name]
self.MockMenus = model('ir.ui.menu') self.MockMenus = model('ir.ui.menu')
@ -28,7 +31,7 @@ class LoadTest(unittest2.TestCase):
}] }]
def tearDown(self): def tearDown(self):
del self.request self.tmp_req.__exit__()
del self.MockMenus del self.MockMenus
del self.menu del self.menu
@ -36,11 +39,11 @@ class LoadTest(unittest2.TestCase):
self.MockMenus.search.return_value = [] self.MockMenus.search.return_value = []
self.MockMenus.read.return_value = [] self.MockMenus.read.return_value = []
root = self.menu.load(self.request) root = self.menu.load()
self.MockMenus.search.assert_called_with( self.MockMenus.search.assert_called_with(
[('parent_id','=', False)], 0, False, False, [('parent_id','=', False)], 0, False, False,
self.request.context) req.context)
self.assertEqual(root['all_menu_ids'], []) self.assertEqual(root['all_menu_ids'], [])
@ -56,16 +59,16 @@ class LoadTest(unittest2.TestCase):
{'id': 2, 'sequence': 3, 'parent_id': False}, {'id': 2, 'sequence': 3, 'parent_id': False},
] ]
root = self.menu.load(self.request) root = self.menu.load()
self.MockMenus.search.assert_called_with( self.MockMenus.search.assert_called_with(
[('id','child_of', [1, 2, 3])], 0, False, False, [('id','child_of', [1, 2, 3])], 0, False, False,
self.request.context) req.context)
self.MockMenus.read.assert_called_with( self.MockMenus.read.assert_called_with(
[1, 2, 3], ['name', 'sequence', 'parent_id', [1, 2, 3], ['name', 'sequence', 'parent_id',
'action'], 'action'],
self.request.context) req.context)
self.assertEqual(root['all_menu_ids'], [1, 2, 3]) self.assertEqual(root['all_menu_ids'], [1, 2, 3])
@ -95,11 +98,11 @@ class LoadTest(unittest2.TestCase):
{'id': 4, 'sequence': 2, 'parent_id': [2, '']}, {'id': 4, 'sequence': 2, 'parent_id': [2, '']},
]) ])
root = self.menu.load(self.request) root = self.menu.load()
self.MockMenus.search.assert_called_with( self.MockMenus.search.assert_called_with(
[('id','child_of', [1])], 0, False, False, [('id','child_of', [1])], 0, False, False,
self.request.context) req.context)
self.assertEqual(root['all_menu_ids'], [1, 2, 3, 4]) self.assertEqual(root['all_menu_ids'], [1, 2, 3, 4])