2011-08-30 23:39:20 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
import datetime
|
2012-01-27 13:17:46 +00:00
|
|
|
import babel
|
2011-08-30 23:39:20 +00:00
|
|
|
import dateutil.relativedelta
|
2011-10-05 17:58:26 +00:00
|
|
|
import logging
|
2011-08-30 23:39:20 +00:00
|
|
|
import time
|
2012-08-15 20:35:04 +00:00
|
|
|
import sys
|
|
|
|
|
2011-09-13 16:09:47 +00:00
|
|
|
import openerplib
|
2011-08-30 23:39:20 +00:00
|
|
|
|
2012-02-10 10:34:26 +00:00
|
|
|
from . import nonliterals
|
2011-09-29 12:17:32 +00:00
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
2012-08-15 17:03:21 +00:00
|
|
|
|
|
|
|
#----------------------------------------------------------
|
|
|
|
# openerplib local connector
|
|
|
|
#----------------------------------------------------------
|
|
|
|
class LibException(Exception):
|
|
|
|
""" Base of all client lib exceptions """
|
|
|
|
def __init__(self,code=None,message=None):
|
|
|
|
self.code = code
|
|
|
|
self.message = message
|
|
|
|
|
|
|
|
class ApplicationError(LibException):
|
|
|
|
""" maps to code: 1, server side: Exception or openerp.exceptions.DeferredException"""
|
|
|
|
|
|
|
|
class Warning(LibException):
|
|
|
|
""" maps to code: 2, server side: openerp.exceptions.Warning"""
|
|
|
|
|
|
|
|
class AccessError(LibException):
|
|
|
|
""" maps to code: 3, server side: openerp.exceptions.AccessError"""
|
|
|
|
|
|
|
|
class AccessDenied(LibException):
|
|
|
|
""" maps to code: 4, server side: openerp.exceptions.AccessDenied"""
|
|
|
|
|
|
|
|
class LocalConnector(openerplib.Connector):
|
|
|
|
"""
|
|
|
|
A type of connector that uses the XMLRPC protocol.
|
|
|
|
"""
|
|
|
|
PROTOCOL = 'local'
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def send(self, service_name, method, *args):
|
|
|
|
import openerp
|
|
|
|
import traceback
|
|
|
|
import xmlrpclib
|
|
|
|
code_string = "warning -- %s\n\n%s"
|
|
|
|
try:
|
|
|
|
return openerp.netsvc.dispatch_rpc(service_name, method, args)
|
|
|
|
except openerp.osv.osv.except_osv, e:
|
|
|
|
# TODO change the except to raise LibException instead of their emulated xmlrpc fault
|
|
|
|
raise xmlrpclib.Fault(code_string % (e.name, e.value), '')
|
|
|
|
except openerp.exceptions.Warning, e:
|
|
|
|
raise xmlrpclib.Fault(code_string % ("Warning", e), '')
|
|
|
|
except openerp.exceptions.AccessError, e:
|
|
|
|
raise xmlrpclib.Fault(code_string % ("AccessError", e), '')
|
|
|
|
except openerp.exceptions.AccessDenied, e:
|
|
|
|
raise xmlrpclib.Fault('AccessDenied', str(e))
|
|
|
|
except openerp.exceptions.DeferredException, e:
|
|
|
|
formatted_info = "".join(traceback.format_exception(*e.traceback))
|
|
|
|
raise xmlrpclib.Fault(openerp.tools.ustr(e.message), formatted_info)
|
|
|
|
except Exception, e:
|
|
|
|
formatted_info = "".join(traceback.format_exception(*(sys.exc_info())))
|
|
|
|
raise xmlrpclib.Fault(openerp.tools.exception_to_unicode(e), formatted_info)
|
|
|
|
|
2011-08-30 23:39:20 +00:00
|
|
|
#----------------------------------------------------------
|
|
|
|
# OpenERPSession RPC openerp backend access
|
|
|
|
#----------------------------------------------------------
|
|
|
|
class OpenERPSession(object):
|
|
|
|
"""
|
|
|
|
An OpenERP RPC session, a given user can own multiple such sessions
|
|
|
|
in a web session.
|
|
|
|
|
|
|
|
.. attribute:: context
|
|
|
|
|
|
|
|
The session context, a ``dict``. Can be reloaded by calling
|
|
|
|
:meth:`openerpweb.openerpweb.OpenERPSession.get_context`
|
|
|
|
|
|
|
|
.. attribute:: domains_store
|
|
|
|
|
|
|
|
A ``dict`` matching domain keys to evaluable (but non-literal) domains.
|
|
|
|
|
|
|
|
Used to store references to non-literal domains which need to be
|
|
|
|
round-tripped to the client browser.
|
|
|
|
"""
|
2011-12-15 12:07:32 +00:00
|
|
|
def __init__(self):
|
2012-01-12 12:52:07 +00:00
|
|
|
self._creation_time = time.time()
|
2011-09-30 13:07:29 +00:00
|
|
|
self.config = None
|
2011-08-30 23:39:20 +00:00
|
|
|
self._db = False
|
|
|
|
self._uid = False
|
|
|
|
self._login = False
|
|
|
|
self._password = False
|
2012-02-10 16:43:09 +00:00
|
|
|
self._suicide = False
|
2011-08-30 23:39:20 +00:00
|
|
|
self.context = {}
|
|
|
|
self.contexts_store = {}
|
|
|
|
self.domains_store = {}
|
2011-10-24 09:07:27 +00:00
|
|
|
self.jsonp_requests = {} # FIXME use a LRU
|
2011-09-30 13:07:29 +00:00
|
|
|
|
|
|
|
def __getstate__(self):
|
|
|
|
state = dict(self.__dict__)
|
|
|
|
if "config" in state:
|
|
|
|
del state['config']
|
|
|
|
return state
|
2011-09-24 16:57:05 +00:00
|
|
|
|
2011-09-13 16:09:47 +00:00
|
|
|
def build_connection(self):
|
2011-09-29 12:17:32 +00:00
|
|
|
conn = openerplib.Connection(self.config.connector, database=self._db, login=self._login,
|
2012-01-06 10:02:51 +00:00
|
|
|
user_id=self._uid, password=self._password)
|
2011-09-24 16:57:05 +00:00
|
|
|
return conn
|
2011-08-30 23:39:20 +00:00
|
|
|
|
|
|
|
def proxy(self, service):
|
2011-09-13 16:09:47 +00:00
|
|
|
return self.build_connection().get_service(service)
|
2011-08-30 23:39:20 +00:00
|
|
|
|
2011-09-16 10:25:55 +00:00
|
|
|
def bind(self, db, uid, login, password):
|
2011-08-30 23:39:20 +00:00
|
|
|
self._db = db
|
|
|
|
self._uid = uid
|
2011-09-16 10:25:55 +00:00
|
|
|
self._login = login
|
2011-08-30 23:39:20 +00:00
|
|
|
self._password = password
|
|
|
|
|
2012-08-12 15:15:32 +00:00
|
|
|
def authenticate(self, db, login, password, env=None):
|
2011-12-13 16:15:54 +00:00
|
|
|
# TODO use the openerplib API once it exposes authenticate()
|
2011-10-13 14:33:39 +00:00
|
|
|
uid = self.proxy('common').authenticate(db, login, password, env)
|
2011-09-16 10:25:55 +00:00
|
|
|
self.bind(db, uid, login, password)
|
2011-08-30 23:39:20 +00:00
|
|
|
|
|
|
|
if uid: self.get_context()
|
|
|
|
return uid
|
|
|
|
|
2011-09-16 10:25:55 +00:00
|
|
|
def assert_valid(self, force=False):
|
2011-08-30 23:39:20 +00:00
|
|
|
"""
|
|
|
|
Ensures this session is valid (logged into the openerp server)
|
|
|
|
"""
|
2011-09-16 10:25:55 +00:00
|
|
|
self.build_connection().check_login(force)
|
2011-08-30 23:39:20 +00:00
|
|
|
|
2012-01-19 14:30:57 +00:00
|
|
|
def ensure_valid(self):
|
|
|
|
if self._uid:
|
|
|
|
try:
|
|
|
|
self.assert_valid(True)
|
|
|
|
except Exception:
|
|
|
|
self._uid = None
|
|
|
|
|
2011-08-30 23:39:20 +00:00
|
|
|
def execute(self, model, func, *l, **d):
|
|
|
|
self.assert_valid()
|
2011-09-13 16:09:47 +00:00
|
|
|
model = self.build_connection().get_model(model)
|
|
|
|
r = getattr(model, func)(*l, **d)
|
2011-08-30 23:39:20 +00:00
|
|
|
return r
|
|
|
|
|
|
|
|
def exec_workflow(self, model, id, signal):
|
|
|
|
self.assert_valid()
|
|
|
|
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
|
|
|
|
return r
|
|
|
|
|
|
|
|
def model(self, model):
|
|
|
|
""" Get an RPC proxy for the object ``model``, bound to this session.
|
|
|
|
|
|
|
|
:param model: an OpenERP model name
|
|
|
|
:type model: str
|
2011-09-13 16:09:47 +00:00
|
|
|
:rtype: a model object
|
2011-08-30 23:39:20 +00:00
|
|
|
"""
|
2011-09-13 16:09:47 +00:00
|
|
|
return self.build_connection().get_model(model)
|
2011-08-30 23:39:20 +00:00
|
|
|
|
|
|
|
def get_context(self):
|
|
|
|
""" Re-initializes the current user's session context (based on
|
|
|
|
his preferences) by calling res.users.get_context() with the old
|
|
|
|
context
|
|
|
|
|
|
|
|
:returns: the new context
|
|
|
|
"""
|
|
|
|
assert self._uid, "The user needs to be logged-in to initialize his context"
|
2012-01-06 10:16:19 +00:00
|
|
|
self.context = self.build_connection().get_user_context() or {}
|
2012-01-26 10:51:52 +00:00
|
|
|
self.context['uid'] = self._uid
|
2012-01-27 13:17:46 +00:00
|
|
|
self._fix_lang(self.context)
|
2011-08-30 23:39:20 +00:00
|
|
|
return self.context
|
|
|
|
|
2012-01-27 13:17:46 +00:00
|
|
|
def _fix_lang(self, context):
|
|
|
|
""" OpenERP provides languages which may not make sense and/or may not
|
|
|
|
be understood by the web client's libraries.
|
|
|
|
|
|
|
|
Fix those here.
|
|
|
|
|
|
|
|
:param dict context: context to fix
|
|
|
|
"""
|
|
|
|
lang = context['lang']
|
|
|
|
|
|
|
|
# inane OpenERP locale
|
|
|
|
if lang == 'ar_AR':
|
|
|
|
lang = 'ar'
|
|
|
|
|
|
|
|
# lang to lang_REGION (datejs only handles lang_REGION, no bare langs)
|
|
|
|
if lang in babel.core.LOCALE_ALIASES:
|
|
|
|
lang = babel.core.LOCALE_ALIASES[lang]
|
|
|
|
|
|
|
|
context['lang'] = lang
|
|
|
|
|
2011-08-30 23:39:20 +00:00
|
|
|
@property
|
|
|
|
def base_eval_context(self):
|
|
|
|
""" Default evaluation context for the session.
|
|
|
|
|
|
|
|
Used to evaluate contexts and domains.
|
|
|
|
"""
|
|
|
|
base = dict(
|
|
|
|
uid=self._uid,
|
|
|
|
current_date=datetime.date.today().strftime('%Y-%m-%d'),
|
|
|
|
time=time,
|
|
|
|
datetime=datetime,
|
|
|
|
relativedelta=dateutil.relativedelta.relativedelta
|
|
|
|
)
|
|
|
|
base.update(self.context)
|
|
|
|
return base
|
|
|
|
|
|
|
|
def evaluation_context(self, context=None):
|
|
|
|
""" Returns the session's evaluation context, augmented with the
|
|
|
|
provided context if any.
|
|
|
|
|
|
|
|
:param dict context: to add merge in the session's base eval context
|
|
|
|
:returns: the augmented context
|
|
|
|
:rtype: dict
|
|
|
|
"""
|
|
|
|
d = dict(self.base_eval_context)
|
|
|
|
if context:
|
|
|
|
d.update(context)
|
|
|
|
d['context'] = d
|
|
|
|
return d
|
|
|
|
|
|
|
|
def eval_context(self, context_to_eval, context=None):
|
|
|
|
""" Evaluates the provided context_to_eval in the context (haha) of
|
|
|
|
the context. Also merges the evaluated context with the session's context.
|
|
|
|
|
|
|
|
:param context_to_eval: a context to evaluate. Must be a dict or a
|
|
|
|
non-literal context. If it's a dict, will be
|
|
|
|
returned as-is
|
|
|
|
:type context_to_eval: openerpweb.nonliterals.Context
|
|
|
|
:returns: the evaluated context
|
|
|
|
:rtype: dict
|
|
|
|
|
|
|
|
:raises: ``TypeError`` if ``context_to_eval`` is neither a dict nor
|
|
|
|
a Context
|
|
|
|
"""
|
|
|
|
ctx = dict(
|
|
|
|
self.base_eval_context,
|
|
|
|
**(context or {}))
|
|
|
|
|
|
|
|
# adding the context of the session to send to the openerp server
|
|
|
|
ccontext = nonliterals.CompoundContext(self.context, context_to_eval or {})
|
|
|
|
ccontext.session = self
|
|
|
|
return ccontext.evaluate(ctx)
|
|
|
|
|
|
|
|
def eval_domain(self, domain, context=None):
|
|
|
|
""" Evaluates the provided domain using the provided context
|
|
|
|
(merged with the session's evaluation context)
|
|
|
|
|
|
|
|
:param domain: an OpenERP domain as a list or as a
|
|
|
|
:class:`openerpweb.nonliterals.Domain` instance
|
|
|
|
|
|
|
|
In the second case, it will be evaluated and returned.
|
|
|
|
:type domain: openerpweb.nonliterals.Domain
|
|
|
|
:param dict context: the context to use in the evaluation, if any.
|
|
|
|
:returns: the evaluated domain
|
|
|
|
:rtype: list
|
|
|
|
|
|
|
|
:raises: ``TypeError`` if ``domain`` is neither a list nor a Domain
|
|
|
|
"""
|
|
|
|
if isinstance(domain, list):
|
|
|
|
return domain
|
|
|
|
|
|
|
|
cdomain = nonliterals.CompoundDomain(domain)
|
|
|
|
cdomain.session = self
|
|
|
|
return cdomain.evaluate(context or {})
|
2012-08-15 17:03:21 +00:00
|
|
|
|
|
|
|
# vim:et:ts=4:sw=4:
|