[IMP] cleanup of web.common
bzr revid: al@openerp.com-20111005175826-7fzk3wesvz198kpm
This commit is contained in:
parent
caec244322
commit
0d79dca93f
|
@ -1,6 +1,5 @@
|
|||
import common
|
||||
import controllers
|
||||
import common.dispatch
|
||||
import logging
|
||||
import optparse
|
||||
|
||||
|
@ -22,6 +21,6 @@ def wsgi_postload():
|
|||
o.serve_static = True
|
||||
o.backend = 'local'
|
||||
|
||||
app = common.dispatch.Root(o)
|
||||
app = common.http.Root(o)
|
||||
openerp.wsgi.register_wsgi_handler(app)
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
#!/usr/bin/python
|
||||
from dispatch import *
|
||||
import http
|
||||
import nonliterals
|
||||
import release
|
||||
import session
|
||||
import xml2json
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
""" Backport of Python 2.6's ast.py for Python 2.5
|
||||
"""
|
||||
__all__ = ['literal_eval']
|
||||
try:
|
||||
from ast import literal_eval
|
||||
except ImportError:
|
||||
from _ast import *
|
||||
from _ast import __version__
|
||||
|
||||
|
||||
def parse(expr, filename='<unknown>', mode='exec'):
|
||||
"""
|
||||
Parse an expression into an AST node.
|
||||
Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
|
||||
"""
|
||||
return compile(expr, filename, mode, PyCF_ONLY_AST)
|
||||
|
||||
|
||||
def literal_eval(node_or_string):
|
||||
"""
|
||||
Safely evaluate an expression node or a string containing a Python
|
||||
expression. The string or node provided may only consist of the
|
||||
following Python literal structures: strings, numbers, tuples, lists,
|
||||
dicts, booleans, and None.
|
||||
"""
|
||||
_safe_names = {'None': None, 'True': True, 'False': False}
|
||||
if isinstance(node_or_string, basestring):
|
||||
node_or_string = parse(node_or_string, mode='eval')
|
||||
if isinstance(node_or_string, Expression):
|
||||
node_or_string = node_or_string.body
|
||||
def _convert(node):
|
||||
if isinstance(node, Str):
|
||||
return node.s
|
||||
elif isinstance(node, Num):
|
||||
return node.n
|
||||
elif isinstance(node, Tuple):
|
||||
return tuple(map(_convert, node.elts))
|
||||
elif isinstance(node, List):
|
||||
return list(map(_convert, node.elts))
|
||||
elif isinstance(node, Dict):
|
||||
return dict((_convert(k), _convert(v)) for k, v
|
||||
in zip(node.keys, node.values))
|
||||
elif isinstance(node, Name):
|
||||
if node.id in _safe_names:
|
||||
return _safe_names[node.id]
|
||||
raise ValueError('malformed string')
|
||||
return _convert(node_or_string)
|
|
@ -1,88 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import datetime
|
||||
|
||||
DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d"
|
||||
DEFAULT_SERVER_TIME_FORMAT = "%H:%M:%S"
|
||||
DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % (
|
||||
DEFAULT_SERVER_DATE_FORMAT,
|
||||
DEFAULT_SERVER_TIME_FORMAT)
|
||||
|
||||
def str_to_datetime(str):
|
||||
"""
|
||||
Converts a string to a datetime object using OpenERP's
|
||||
datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
|
||||
No timezone information is added, the datetime is a naive instance, but
|
||||
according to OpenERP 6.1 specification the timezone is always UTC.
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
def str_to_date(str):
|
||||
"""
|
||||
Converts a string to a date object using OpenERP's
|
||||
date string format (exemple: '2011-12-01').
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATE_FORMAT).date()
|
||||
|
||||
def str_to_time(str):
|
||||
"""
|
||||
Converts a string to a time object using OpenERP's
|
||||
time string format (exemple: '15:12:35').
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_TIME_FORMAT).time()
|
||||
|
||||
def datetime_to_str(obj):
|
||||
"""
|
||||
Converts a datetime object to a string using OpenERP's
|
||||
datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
|
||||
The datetime instance should not have an attached timezone and be in UTC.
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
def date_to_str(obj):
|
||||
"""
|
||||
Converts a date object to a string using OpenERP's
|
||||
date string format (exemple: '2011-12-01').
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
def time_to_str(obj):
|
||||
"""
|
||||
Converts a time object to a string using OpenERP's
|
||||
time string format (exemple: '15:12:35').
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_TIME_FORMAT)
|
|
@ -1,428 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
from __future__ import with_statement
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import urllib
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import traceback
|
||||
import uuid
|
||||
import xmlrpclib
|
||||
|
||||
import simplejson
|
||||
import werkzeug.datastructures
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
|
||||
import ast
|
||||
import nonliterals
|
||||
import http
|
||||
import session
|
||||
import openerplib
|
||||
|
||||
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
||||
'WebRequest', 'JsonRequest', 'HttpRequest']
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Globals (wont move into a pool)
|
||||
#-----------------------------------------------------------
|
||||
|
||||
addons_module = {}
|
||||
addons_manifest = {}
|
||||
controllers_class = {}
|
||||
controllers_object = {}
|
||||
controllers_path = {}
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web RequestHandler
|
||||
#----------------------------------------------------------
|
||||
class WebRequest(object):
|
||||
""" Parent class for all OpenERP Web request types, mostly deals with
|
||||
initialization and setup of the request object (the dispatching itself has
|
||||
to be handled by the subclasses)
|
||||
|
||||
:param request: a wrapped werkzeug Request object
|
||||
:type request: :class:`werkzeug.wrappers.BaseRequest`
|
||||
:param config: configuration object
|
||||
|
||||
.. attribute:: httprequest
|
||||
|
||||
the original :class:`werkzeug.wrappers.Request` object provided to the
|
||||
request
|
||||
|
||||
.. attribute:: httpsession
|
||||
|
||||
a :class:`~collections.Mapping` holding the HTTP session data for the
|
||||
current http session
|
||||
|
||||
.. attribute:: config
|
||||
|
||||
config parameter provided to the request object
|
||||
|
||||
.. attribute:: params
|
||||
|
||||
:class:`~collections.Mapping` of request parameters, not generally
|
||||
useful as they're provided directly to the handler method as keyword
|
||||
arguments
|
||||
|
||||
.. attribute:: session_id
|
||||
|
||||
opaque identifier for the :class:`session.OpenERPSession` instance of
|
||||
the current request
|
||||
|
||||
.. attribute:: session
|
||||
|
||||
:class:`~session.OpenERPSession` instance for the current request
|
||||
|
||||
.. attribute:: context
|
||||
|
||||
:class:`~collections.Mapping` of context values for the current request
|
||||
|
||||
.. attribute:: debug
|
||||
|
||||
``bool``, indicates whether the debug mode is active on the client
|
||||
"""
|
||||
def __init__(self, request, config):
|
||||
self.httprequest = request
|
||||
self.httpresponse = None
|
||||
self.httpsession = request.session
|
||||
self.config = config
|
||||
|
||||
def init(self, params):
|
||||
self.params = dict(params)
|
||||
# OpenERP session setup
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
|
||||
self.session.config = self.config
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
class JsonRequest(WebRequest):
|
||||
""" JSON-RPC2 over HTTP.
|
||||
|
||||
Sucessful request::
|
||||
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"result": { "res1": "val1" },
|
||||
"id": null}
|
||||
|
||||
Request producing a error::
|
||||
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"error": {"code": 1,
|
||||
"message": "End user error message.",
|
||||
"data": {"code": "codestring",
|
||||
"debug": "traceback" } },
|
||||
"id": null}
|
||||
|
||||
"""
|
||||
|
||||
def dispatch(self, controller, method, requestf=None, request=None):
|
||||
""" Calls the method asked for by the JSON-RPC2 request
|
||||
|
||||
:param controller: the instance of the controller 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
|
||||
"""
|
||||
response = {"jsonrpc": "2.0" }
|
||||
error = None
|
||||
try:
|
||||
# Read POST content or POST Form Data named "request"
|
||||
if requestf:
|
||||
self.jsonrequest = simplejson.load(requestf, object_hook=nonliterals.non_literal_decoder)
|
||||
else:
|
||||
self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
|
||||
self.init(self.jsonrequest.get("params", {}))
|
||||
if _logger.isEnabledFor(logging.DEBUG):
|
||||
_logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest))
|
||||
response['id'] = self.jsonrequest.get('id')
|
||||
response["result"] = method(controller, self, **self.params)
|
||||
except openerplib.AuthenticationError:
|
||||
error = {
|
||||
'code': 100,
|
||||
'message': "OpenERP Session Invalid",
|
||||
'data': {
|
||||
'type': 'session_invalid',
|
||||
'debug': traceback.format_exc()
|
||||
}
|
||||
}
|
||||
except xmlrpclib.Fault, e:
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': "OpenERP Server Error",
|
||||
'data': {
|
||||
'type': 'server_exception',
|
||||
'fault_code': e.faultCode,
|
||||
'debug': "Client %s\nServer %s" % (
|
||||
"".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
|
||||
}
|
||||
}
|
||||
except Exception:
|
||||
logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
|
||||
("An error occured while handling a json request")
|
||||
error = {
|
||||
'code': 300,
|
||||
'message': "OpenERP WebClient Error",
|
||||
'data': {
|
||||
'type': 'client_exception',
|
||||
'debug': "Client %s" % traceback.format_exc()
|
||||
}
|
||||
}
|
||||
if error:
|
||||
response["error"] = error
|
||||
|
||||
if _logger.isEnabledFor(logging.DEBUG):
|
||||
_logger.debug("<--\n%s", pprint.pformat(response))
|
||||
content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
|
||||
return werkzeug.wrappers.Response(
|
||||
content, headers=[('Content-Type', 'application/json'),
|
||||
('Content-Length', len(content))])
|
||||
|
||||
def jsonrequest(f):
|
||||
""" Decorator marking the decorated method as being a handler for a
|
||||
JSON-RPC request (the exact request path is specified via the
|
||||
``$(Controller._cp_path)/$methodname`` combination.
|
||||
|
||||
If the method is called, it will be provided with a :class:`JsonRequest`
|
||||
instance and all ``params`` sent during the JSON-RPC request, apart from
|
||||
the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
|
||||
beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def json_handler(controller, request, config):
|
||||
return JsonRequest(request, config).dispatch(
|
||||
controller, f, requestf=request.stream)
|
||||
json_handler.exposed = True
|
||||
return json_handler
|
||||
|
||||
class HttpRequest(WebRequest):
|
||||
""" Regular GET/POST request
|
||||
"""
|
||||
def dispatch(self, controller, method):
|
||||
params = dict(self.httprequest.args)
|
||||
params.update(self.httprequest.form)
|
||||
params.update(self.httprequest.files)
|
||||
self.init(params)
|
||||
akw = {}
|
||||
for key, value in self.httprequest.args.iteritems():
|
||||
if isinstance(value, basestring) and len(value) < 1024:
|
||||
akw[key] = value
|
||||
else:
|
||||
akw[key] = type(value)
|
||||
_logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
|
||||
r = method(controller, self, **self.params)
|
||||
if self.debug or 1:
|
||||
if isinstance(r, werkzeug.wrappers.BaseResponse):
|
||||
_logger.debug('<-- %s', r)
|
||||
else:
|
||||
_logger.debug("<-- size: %s", len(r))
|
||||
return r
|
||||
|
||||
def make_response(self, data, headers=None, cookies=None):
|
||||
""" Helper for non-HTML responses, or HTML responses with custom
|
||||
response headers or cookies.
|
||||
|
||||
While handlers can just return the HTML markup of a page they want to
|
||||
send as a string if non-HTML data is returned they need to create a
|
||||
complete response object, or the returned data will not be correctly
|
||||
interpreted by the clients.
|
||||
|
||||
:param basestring data: response body
|
||||
:param headers: HTTP headers to set on the response
|
||||
:type headers: ``[(name, value)]``
|
||||
:param collections.Mapping cookies: cookies to set on the client
|
||||
"""
|
||||
response = werkzeug.wrappers.Response(data, headers=headers)
|
||||
if cookies:
|
||||
for k, v in cookies.iteritems():
|
||||
response.set_cookie(k, v)
|
||||
return response
|
||||
|
||||
def not_found(self, description=None):
|
||||
""" Helper for 404 response, return its result from the method
|
||||
"""
|
||||
return werkzeug.exceptions.NotFound(description)
|
||||
|
||||
def httprequest(f):
|
||||
""" Decorator marking the decorated method as being a handler for a
|
||||
normal HTTP request (the exact request path is specified via the
|
||||
``$(Controller._cp_path)/$methodname`` combination.
|
||||
|
||||
If the method is called, it will be provided with a :class:`HttpRequest`
|
||||
instance and all ``params`` sent during the request (``GET`` and ``POST``
|
||||
merged in the same dictionary), apart from the ``session_id``, ``context``
|
||||
and ``debug`` keys (which are stripped out beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def http_handler(controller, request, config):
|
||||
return HttpRequest(request, config).dispatch(controller, f)
|
||||
http_handler.exposed = True
|
||||
return http_handler
|
||||
|
||||
class ControllerType(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(ControllerType, cls).__init__(name, bases, attrs)
|
||||
controllers_class["%s.%s" % (cls.__module__, cls.__name__)] = cls
|
||||
|
||||
class Controller(object):
|
||||
__metaclass__ = ControllerType
|
||||
|
||||
class Root(object):
|
||||
"""Root WSGI application for the OpenERP Web Client.
|
||||
|
||||
:param options: mandatory initialization options object, must provide
|
||||
the following attributes:
|
||||
|
||||
``server_host`` (``str``)
|
||||
hostname of the OpenERP server to dispatch RPC to
|
||||
``server_port`` (``int``)
|
||||
RPC port of the OpenERP server
|
||||
``serve_static`` (``bool | None``)
|
||||
whether this application should serve the various
|
||||
addons's static files
|
||||
``storage_path`` (``str``)
|
||||
filesystem path where HTTP session data will be stored
|
||||
``dbfilter`` (``str``)
|
||||
only used in case the list of databases is requested
|
||||
by the server, will be filtered by this pattern
|
||||
"""
|
||||
def __init__(self, options):
|
||||
self.root = '/web/webclient/home'
|
||||
self.config = options
|
||||
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connector(protocol='local')
|
||||
else:
|
||||
conn = openerplib.get_connector(hostname=self.config.server_host,
|
||||
port=self.config.server_port)
|
||||
self.config.connector = conn
|
||||
|
||||
self.session_cookie = 'sessionid'
|
||||
self.addons = {}
|
||||
|
||||
static_dirs = self._load_addons()
|
||||
if options.serve_static:
|
||||
self.dispatch = werkzeug.wsgi.SharedDataMiddleware(
|
||||
self.dispatch, static_dirs)
|
||||
|
||||
if options.session_storage:
|
||||
if not os.path.exists(options.session_storage):
|
||||
os.mkdir(options.session_storage, 0700)
|
||||
self.session_storage = options.session_storage
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
""" Handle a WSGI request
|
||||
"""
|
||||
return self.dispatch(environ, start_response)
|
||||
|
||||
def dispatch(self, environ, start_response):
|
||||
"""
|
||||
Performs the actual WSGI dispatching for the application, may be
|
||||
wrapped during the initialization of the object.
|
||||
|
||||
Call the object directly.
|
||||
"""
|
||||
request = werkzeug.wrappers.Request(environ)
|
||||
request.parameter_storage_class = werkzeug.datastructures.ImmutableDict
|
||||
|
||||
if request.path == '/':
|
||||
params = urllib.urlencode(dict(request.args, debug=''))
|
||||
return werkzeug.utils.redirect(self.root + '?' + params, 301)(
|
||||
environ, start_response)
|
||||
elif request.path == '/mobile':
|
||||
return werkzeug.utils.redirect(
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)
|
||||
|
||||
handler = self.find_handler(*(request.path.split('/')[1:]))
|
||||
|
||||
if not handler:
|
||||
response = werkzeug.exceptions.NotFound()
|
||||
else:
|
||||
with http.session(request, self.session_storage, self.session_cookie) as session:
|
||||
result = handler(
|
||||
request, self.config)
|
||||
|
||||
if isinstance(result, basestring):
|
||||
response = werkzeug.wrappers.Response(
|
||||
result, headers=[('Content-Type', 'text/html; charset=utf-8'),
|
||||
('Content-Length', len(result))])
|
||||
else:
|
||||
response = result
|
||||
|
||||
response.set_cookie(self.session_cookie, session.sid)
|
||||
|
||||
return response(environ, start_response)
|
||||
|
||||
def _load_addons(self):
|
||||
"""
|
||||
Loads all addons at the specified addons path, returns a mapping of
|
||||
static URLs to the corresponding directories
|
||||
"""
|
||||
statics = {}
|
||||
for addons_path in self.config.addons_path:
|
||||
if addons_path not in sys.path:
|
||||
sys.path.insert(0, addons_path)
|
||||
for module in os.listdir(addons_path):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||
path_static = os.path.join(addons_path, module, 'static')
|
||||
if os.path.isfile(manifest_path) and os.path.isdir(path_static):
|
||||
manifest = ast.literal_eval(open(manifest_path).read())
|
||||
manifest['addons_path'] = addons_path
|
||||
_logger.info("Loading %s", module)
|
||||
m = __import__(module)
|
||||
addons_module[module] = m
|
||||
addons_manifest[module] = manifest
|
||||
statics['/%s/static' % module] = path_static
|
||||
for k, v in controllers_class.items():
|
||||
if k not in controllers_object:
|
||||
o = v()
|
||||
controllers_object[k] = o
|
||||
if hasattr(o, '_cp_path'):
|
||||
controllers_path[o._cp_path] = o
|
||||
return statics
|
||||
|
||||
def find_handler(self, *l):
|
||||
"""
|
||||
Tries to discover the controller handling the request for the path
|
||||
specified by the provided parameters
|
||||
|
||||
:param l: path sections to a controller or controller method
|
||||
:returns: a callable matching the path sections, or ``None``
|
||||
:rtype: ``Controller | None``
|
||||
"""
|
||||
if len(l) > 1:
|
||||
for i in range(len(l), 1, -1):
|
||||
ps = "/" + "/".join(l[0:i])
|
||||
if ps in controllers_path:
|
||||
c = controllers_path[ps]
|
||||
rest = l[i:] or ['index']
|
||||
meth = rest[0]
|
||||
m = getattr(c, meth)
|
||||
if getattr(m, 'exposed', False):
|
||||
_logger.debug("Dispatching to %s %s %s", ps, c, meth)
|
||||
return m
|
||||
return None
|
|
@ -1,13 +1,286 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web HTTP layer
|
||||
#----------------------------------------------------------
|
||||
import ast
|
||||
import contextlib
|
||||
import functools
|
||||
import logging
|
||||
import urllib
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import traceback
|
||||
import uuid
|
||||
import xmlrpclib
|
||||
|
||||
import simplejson
|
||||
import werkzeug.contrib.sessions
|
||||
import werkzeug.datastructures
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
|
||||
import nonliterals
|
||||
import session
|
||||
import openerplib
|
||||
|
||||
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
||||
'WebRequest', 'JsonRequest', 'HttpRequest']
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web RequestHandler
|
||||
#----------------------------------------------------------
|
||||
class WebRequest(object):
|
||||
""" Parent class for all OpenERP Web request types, mostly deals with
|
||||
initialization and setup of the request object (the dispatching itself has
|
||||
to be handled by the subclasses)
|
||||
|
||||
:param request: a wrapped werkzeug Request object
|
||||
:type request: :class:`werkzeug.wrappers.BaseRequest`
|
||||
:param config: configuration object
|
||||
|
||||
.. attribute:: httprequest
|
||||
|
||||
the original :class:`werkzeug.wrappers.Request` object provided to the
|
||||
request
|
||||
|
||||
.. attribute:: httpsession
|
||||
|
||||
a :class:`~collections.Mapping` holding the HTTP session data for the
|
||||
current http session
|
||||
|
||||
.. attribute:: config
|
||||
|
||||
config parameter provided to the request object
|
||||
|
||||
.. attribute:: params
|
||||
|
||||
:class:`~collections.Mapping` of request parameters, not generally
|
||||
useful as they're provided directly to the handler method as keyword
|
||||
arguments
|
||||
|
||||
.. attribute:: session_id
|
||||
|
||||
opaque identifier for the :class:`session.OpenERPSession` instance of
|
||||
the current request
|
||||
|
||||
.. attribute:: session
|
||||
|
||||
:class:`~session.OpenERPSession` instance for the current request
|
||||
|
||||
.. attribute:: context
|
||||
|
||||
:class:`~collections.Mapping` of context values for the current request
|
||||
|
||||
.. attribute:: debug
|
||||
|
||||
``bool``, indicates whether the debug mode is active on the client
|
||||
"""
|
||||
def __init__(self, request, config):
|
||||
self.httprequest = request
|
||||
self.httpresponse = None
|
||||
self.httpsession = request.session
|
||||
self.config = config
|
||||
|
||||
def init(self, params):
|
||||
self.params = dict(params)
|
||||
# OpenERP session setup
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
|
||||
self.session.config = self.config
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
class JsonRequest(WebRequest):
|
||||
""" JSON-RPC2 over HTTP.
|
||||
|
||||
Sucessful request::
|
||||
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"result": { "res1": "val1" },
|
||||
"id": null}
|
||||
|
||||
Request producing a error::
|
||||
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"error": {"code": 1,
|
||||
"message": "End user error message.",
|
||||
"data": {"code": "codestring",
|
||||
"debug": "traceback" } },
|
||||
"id": null}
|
||||
|
||||
"""
|
||||
|
||||
def dispatch(self, controller, method, requestf=None, request=None):
|
||||
""" Calls the method asked for by the JSON-RPC2 request
|
||||
|
||||
:param controller: the instance of the controller 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
|
||||
"""
|
||||
response = {"jsonrpc": "2.0" }
|
||||
error = None
|
||||
try:
|
||||
# Read POST content or POST Form Data named "request"
|
||||
if requestf:
|
||||
self.jsonrequest = simplejson.load(requestf, object_hook=nonliterals.non_literal_decoder)
|
||||
else:
|
||||
self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
|
||||
self.init(self.jsonrequest.get("params", {}))
|
||||
if _logger.isEnabledFor(logging.DEBUG):
|
||||
_logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest))
|
||||
response['id'] = self.jsonrequest.get('id')
|
||||
response["result"] = method(controller, self, **self.params)
|
||||
except openerplib.AuthenticationError:
|
||||
error = {
|
||||
'code': 100,
|
||||
'message': "OpenERP Session Invalid",
|
||||
'data': {
|
||||
'type': 'session_invalid',
|
||||
'debug': traceback.format_exc()
|
||||
}
|
||||
}
|
||||
except xmlrpclib.Fault, e:
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': "OpenERP Server Error",
|
||||
'data': {
|
||||
'type': 'server_exception',
|
||||
'fault_code': e.faultCode,
|
||||
'debug': "Client %s\nServer %s" % (
|
||||
"".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
|
||||
}
|
||||
}
|
||||
except Exception:
|
||||
logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
|
||||
("An error occured while handling a json request")
|
||||
error = {
|
||||
'code': 300,
|
||||
'message': "OpenERP WebClient Error",
|
||||
'data': {
|
||||
'type': 'client_exception',
|
||||
'debug': "Client %s" % traceback.format_exc()
|
||||
}
|
||||
}
|
||||
if error:
|
||||
response["error"] = error
|
||||
|
||||
if _logger.isEnabledFor(logging.DEBUG):
|
||||
_logger.debug("<--\n%s", pprint.pformat(response))
|
||||
content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
|
||||
return werkzeug.wrappers.Response(
|
||||
content, headers=[('Content-Type', 'application/json'),
|
||||
('Content-Length', len(content))])
|
||||
|
||||
def jsonrequest(f):
|
||||
""" Decorator marking the decorated method as being a handler for a
|
||||
JSON-RPC request (the exact request path is specified via the
|
||||
``$(Controller._cp_path)/$methodname`` combination.
|
||||
|
||||
If the method is called, it will be provided with a :class:`JsonRequest`
|
||||
instance and all ``params`` sent during the JSON-RPC request, apart from
|
||||
the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
|
||||
beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def json_handler(controller, request, config):
|
||||
return JsonRequest(request, config).dispatch(
|
||||
controller, f, requestf=request.stream)
|
||||
json_handler.exposed = True
|
||||
return json_handler
|
||||
|
||||
class HttpRequest(WebRequest):
|
||||
""" Regular GET/POST request
|
||||
"""
|
||||
def dispatch(self, controller, method):
|
||||
params = dict(self.httprequest.args)
|
||||
params.update(self.httprequest.form)
|
||||
params.update(self.httprequest.files)
|
||||
self.init(params)
|
||||
akw = {}
|
||||
for key, value in self.httprequest.args.iteritems():
|
||||
if isinstance(value, basestring) and len(value) < 1024:
|
||||
akw[key] = value
|
||||
else:
|
||||
akw[key] = type(value)
|
||||
_logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
|
||||
r = method(controller, self, **self.params)
|
||||
if self.debug or 1:
|
||||
if isinstance(r, werkzeug.wrappers.BaseResponse):
|
||||
_logger.debug('<-- %s', r)
|
||||
else:
|
||||
_logger.debug("<-- size: %s", len(r))
|
||||
return r
|
||||
|
||||
def make_response(self, data, headers=None, cookies=None):
|
||||
""" Helper for non-HTML responses, or HTML responses with custom
|
||||
response headers or cookies.
|
||||
|
||||
While handlers can just return the HTML markup of a page they want to
|
||||
send as a string if non-HTML data is returned they need to create a
|
||||
complete response object, or the returned data will not be correctly
|
||||
interpreted by the clients.
|
||||
|
||||
:param basestring data: response body
|
||||
:param headers: HTTP headers to set on the response
|
||||
:type headers: ``[(name, value)]``
|
||||
:param collections.Mapping cookies: cookies to set on the client
|
||||
"""
|
||||
response = werkzeug.wrappers.Response(data, headers=headers)
|
||||
if cookies:
|
||||
for k, v in cookies.iteritems():
|
||||
response.set_cookie(k, v)
|
||||
return response
|
||||
|
||||
def not_found(self, description=None):
|
||||
""" Helper for 404 response, return its result from the method
|
||||
"""
|
||||
return werkzeug.exceptions.NotFound(description)
|
||||
|
||||
def httprequest(f):
|
||||
""" Decorator marking the decorated method as being a handler for a
|
||||
normal HTTP request (the exact request path is specified via the
|
||||
``$(Controller._cp_path)/$methodname`` combination.
|
||||
|
||||
If the method is called, it will be provided with a :class:`HttpRequest`
|
||||
instance and all ``params`` sent during the request (``GET`` and ``POST``
|
||||
merged in the same dictionary), apart from the ``session_id``, ``context``
|
||||
and ``debug`` keys (which are stripped out beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def http_handler(controller, request, config):
|
||||
return HttpRequest(request, config).dispatch(controller, f)
|
||||
http_handler.exposed = True
|
||||
return http_handler
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web werkzeug Session Managment wraped using with
|
||||
#----------------------------------------------------------
|
||||
STORES = {}
|
||||
|
||||
@contextlib.contextmanager
|
||||
def session(request, storage_path, session_cookie='sessionid'):
|
||||
def session_context(request, storage_path, session_cookie='sessionid'):
|
||||
session_store = STORES.get(storage_path)
|
||||
if not session_store:
|
||||
session_store = werkzeug.contrib.sessions.FilesystemSessionStore(
|
||||
|
@ -24,3 +297,157 @@ def session(request, storage_path, session_cookie='sessionid'):
|
|||
yield request.session
|
||||
finally:
|
||||
session_store.save(request.session)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web Module/Controller Loading and URL Routing
|
||||
#----------------------------------------------------------
|
||||
addons_module = {}
|
||||
addons_manifest = {}
|
||||
controllers_class = {}
|
||||
controllers_object = {}
|
||||
controllers_path = {}
|
||||
|
||||
class ControllerType(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(ControllerType, cls).__init__(name, bases, attrs)
|
||||
controllers_class["%s.%s" % (cls.__module__, cls.__name__)] = cls
|
||||
|
||||
class Controller(object):
|
||||
__metaclass__ = ControllerType
|
||||
|
||||
class Root(object):
|
||||
"""Root WSGI application for the OpenERP Web Client.
|
||||
|
||||
:param options: mandatory initialization options object, must provide
|
||||
the following attributes:
|
||||
|
||||
``server_host`` (``str``)
|
||||
hostname of the OpenERP server to dispatch RPC to
|
||||
``server_port`` (``int``)
|
||||
RPC port of the OpenERP server
|
||||
``serve_static`` (``bool | None``)
|
||||
whether this application should serve the various
|
||||
addons's static files
|
||||
``storage_path`` (``str``)
|
||||
filesystem path where HTTP session data will be stored
|
||||
``dbfilter`` (``str``)
|
||||
only used in case the list of databases is requested
|
||||
by the server, will be filtered by this pattern
|
||||
"""
|
||||
def __init__(self, options):
|
||||
self.root = '/web/webclient/home'
|
||||
self.config = options
|
||||
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connector(protocol='local')
|
||||
else:
|
||||
conn = openerplib.get_connector(hostname=self.config.server_host,
|
||||
port=self.config.server_port)
|
||||
self.config.connector = conn
|
||||
|
||||
self.session_cookie = 'sessionid'
|
||||
self.addons = {}
|
||||
|
||||
static_dirs = self._load_addons()
|
||||
if options.serve_static:
|
||||
self.dispatch = werkzeug.wsgi.SharedDataMiddleware(
|
||||
self.dispatch, static_dirs)
|
||||
|
||||
if options.session_storage:
|
||||
if not os.path.exists(options.session_storage):
|
||||
os.mkdir(options.session_storage, 0700)
|
||||
self.session_storage = options.session_storage
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
""" Handle a WSGI request
|
||||
"""
|
||||
return self.dispatch(environ, start_response)
|
||||
|
||||
def dispatch(self, environ, start_response):
|
||||
"""
|
||||
Performs the actual WSGI dispatching for the application, may be
|
||||
wrapped during the initialization of the object.
|
||||
|
||||
Call the object directly.
|
||||
"""
|
||||
request = werkzeug.wrappers.Request(environ)
|
||||
request.parameter_storage_class = werkzeug.datastructures.ImmutableDict
|
||||
|
||||
if request.path == '/':
|
||||
params = urllib.urlencode(dict(request.args, debug=''))
|
||||
return werkzeug.utils.redirect(self.root + '?' + params, 301)(
|
||||
environ, start_response)
|
||||
elif request.path == '/mobile':
|
||||
return werkzeug.utils.redirect(
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)
|
||||
|
||||
handler = self.find_handler(*(request.path.split('/')[1:]))
|
||||
|
||||
if not handler:
|
||||
response = werkzeug.exceptions.NotFound()
|
||||
else:
|
||||
with session_context(request, self.session_storage, self.session_cookie) as session:
|
||||
result = handler( request, self.config)
|
||||
|
||||
if isinstance(result, basestring):
|
||||
headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
|
||||
response = werkzeug.wrappers.Response(result, headers=headers)
|
||||
else:
|
||||
response = result
|
||||
|
||||
response.set_cookie(self.session_cookie, session.sid)
|
||||
|
||||
return response(environ, start_response)
|
||||
|
||||
def _load_addons(self):
|
||||
"""
|
||||
Loads all addons at the specified addons path, returns a mapping of
|
||||
static URLs to the corresponding directories
|
||||
"""
|
||||
statics = {}
|
||||
for addons_path in self.config.addons_path:
|
||||
if addons_path not in sys.path:
|
||||
sys.path.insert(0, addons_path)
|
||||
for module in os.listdir(addons_path):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||
path_static = os.path.join(addons_path, module, 'static')
|
||||
if os.path.isfile(manifest_path) and os.path.isdir(path_static):
|
||||
manifest = ast.literal_eval(open(manifest_path).read())
|
||||
manifest['addons_path'] = addons_path
|
||||
_logger.info("Loading %s", module)
|
||||
m = __import__(module)
|
||||
addons_module[module] = m
|
||||
addons_manifest[module] = manifest
|
||||
statics['/%s/static' % module] = path_static
|
||||
for k, v in controllers_class.items():
|
||||
if k not in controllers_object:
|
||||
o = v()
|
||||
controllers_object[k] = o
|
||||
if hasattr(o, '_cp_path'):
|
||||
controllers_path[o._cp_path] = o
|
||||
return statics
|
||||
|
||||
def find_handler(self, *l):
|
||||
"""
|
||||
Tries to discover the controller handling the request for the path
|
||||
specified by the provided parameters
|
||||
|
||||
:param l: path sections to a controller or controller method
|
||||
:returns: a callable matching the path sections, or ``None``
|
||||
:rtype: ``Controller | None``
|
||||
"""
|
||||
if len(l) > 1:
|
||||
for i in range(len(l), 1, -1):
|
||||
ps = "/" + "/".join(l[0:i])
|
||||
if ps in controllers_path:
|
||||
c = controllers_path[ps]
|
||||
rest = l[i:] or ['index']
|
||||
meth = rest[0]
|
||||
m = getattr(c, meth)
|
||||
if getattr(m, 'exposed', False):
|
||||
_logger.debug("Dispatching to %s %s %s", ps, c, meth)
|
||||
return m
|
||||
return None
|
||||
|
||||
#
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
#!/usr/bin/python
|
||||
import datetime
|
||||
import dateutil.relativedelta
|
||||
import logging
|
||||
import time
|
||||
import openerplib
|
||||
|
||||
import nonliterals
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
#----------------------------------------------------------
|
||||
# OpenERPSession RPC openerp backend access
|
||||
#----------------------------------------------------------
|
||||
|
||||
class OpenERPSession(object):
|
||||
"""
|
||||
An OpenERP RPC session, a given user can own multiple such sessions
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import ast
|
||||
import base64
|
||||
import csv
|
||||
import glob
|
||||
|
@ -9,60 +10,16 @@ import os
|
|||
import re
|
||||
import simplejson
|
||||
import textwrap
|
||||
import xmlrpclib
|
||||
import time
|
||||
import xmlrpclib
|
||||
import zlib
|
||||
from xml.etree import ElementTree
|
||||
from cStringIO import StringIO
|
||||
|
||||
from babel.messages.pofile import read_po
|
||||
import babel.messages.pofile
|
||||
|
||||
import web.common.dispatch as openerpweb
|
||||
import web.common.ast
|
||||
import web.common.nonliterals
|
||||
import web.common.release
|
||||
openerpweb.ast = web.common.ast
|
||||
openerpweb.nonliterals = web.common.nonliterals
|
||||
|
||||
|
||||
# Should move to web.common.xml2json.Xml2Json
|
||||
class Xml2Json:
|
||||
# xml2json-direct
|
||||
# Simple and straightforward XML-to-JSON converter in Python
|
||||
# New BSD Licensed
|
||||
#
|
||||
# URL: http://code.google.com/p/xml2json-direct/
|
||||
@staticmethod
|
||||
def convert_to_json(s):
|
||||
return simplejson.dumps(
|
||||
Xml2Json.convert_to_structure(s), sort_keys=True, indent=4)
|
||||
|
||||
@staticmethod
|
||||
def convert_to_structure(s):
|
||||
root = ElementTree.fromstring(s)
|
||||
return Xml2Json.convert_element(root)
|
||||
|
||||
@staticmethod
|
||||
def convert_element(el, skip_whitespaces=True):
|
||||
res = {}
|
||||
if el.tag[0] == "{":
|
||||
ns, name = el.tag.rsplit("}", 1)
|
||||
res["tag"] = name
|
||||
res["namespace"] = ns[1:]
|
||||
else:
|
||||
res["tag"] = el.tag
|
||||
res["attrs"] = {}
|
||||
for k, v in el.items():
|
||||
res["attrs"][k] = v
|
||||
kids = []
|
||||
if el.text and (not skip_whitespaces or el.text.strip() != ''):
|
||||
kids.append(el.text)
|
||||
for kid in el:
|
||||
kids.append(Xml2Json.convert_element(kid))
|
||||
if kid.tail and (not skip_whitespaces or kid.tail.strip() != ''):
|
||||
kids.append(kid.tail)
|
||||
res["children"] = kids
|
||||
return res
|
||||
import web.common
|
||||
openerpweb = web.common.http
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web web Controllers
|
||||
|
@ -200,7 +157,7 @@ class WebClient(openerpweb.Controller):
|
|||
continue
|
||||
try:
|
||||
with open(f_name) as t_file:
|
||||
po = read_po(t_file)
|
||||
po = babel.messages.pofile.read_po(t_file)
|
||||
except:
|
||||
continue
|
||||
for x in po:
|
||||
|
@ -398,8 +355,8 @@ class Session(openerpweb.Controller):
|
|||
no group by should be performed)
|
||||
"""
|
||||
context, domain = eval_context_and_domain(req.session,
|
||||
openerpweb.nonliterals.CompoundContext(*(contexts or [])),
|
||||
openerpweb.nonliterals.CompoundDomain(*(domains or [])))
|
||||
web.common.nonliterals.CompoundContext(*(contexts or [])),
|
||||
web.common.nonliterals.CompoundDomain(*(domains or [])))
|
||||
|
||||
group_by_sequence = []
|
||||
for candidate in (group_by_seq or []):
|
||||
|
@ -816,7 +773,7 @@ class View(openerpweb.Controller):
|
|||
xml = self.transform_view(arch, session, evaluation_context)
|
||||
else:
|
||||
xml = ElementTree.fromstring(arch)
|
||||
fvg['arch'] = Xml2Json.convert_element(xml)
|
||||
fvg['arch'] = web.common.xml2json.Xml2Json.convert_element(xml)
|
||||
|
||||
for field in fvg['fields'].itervalues():
|
||||
if field.get('views'):
|
||||
|
@ -881,7 +838,7 @@ class View(openerpweb.Controller):
|
|||
|
||||
def parse_domain(self, domain, session):
|
||||
""" Parses an arbitrary string containing a domain, transforms it
|
||||
to either a literal domain or a :class:`openerpweb.nonliterals.Domain`
|
||||
to either a literal domain or a :class:`web.common.nonliterals.Domain`
|
||||
|
||||
:param domain: the domain to parse, if the domain is not a string it
|
||||
is assumed to be a literal domain and is returned as-is
|
||||
|
@ -891,14 +848,14 @@ class View(openerpweb.Controller):
|
|||
if not isinstance(domain, (str, unicode)):
|
||||
return domain
|
||||
try:
|
||||
return openerpweb.ast.literal_eval(domain)
|
||||
return ast.literal_eval(domain)
|
||||
except ValueError:
|
||||
# not a literal
|
||||
return openerpweb.nonliterals.Domain(session, domain)
|
||||
return web.common.nonliterals.Domain(session, domain)
|
||||
|
||||
def parse_context(self, context, session):
|
||||
""" Parses an arbitrary string containing a context, transforms it
|
||||
to either a literal context or a :class:`openerpweb.nonliterals.Context`
|
||||
to either a literal context or a :class:`web.common.nonliterals.Context`
|
||||
|
||||
:param context: the context to parse, if the context is not a string it
|
||||
is assumed to be a literal domain and is returned as-is
|
||||
|
@ -908,9 +865,9 @@ class View(openerpweb.Controller):
|
|||
if not isinstance(context, (str, unicode)):
|
||||
return context
|
||||
try:
|
||||
return openerpweb.ast.literal_eval(context)
|
||||
return ast.literal_eval(context)
|
||||
except ValueError:
|
||||
return openerpweb.nonliterals.Context(session, context)
|
||||
return web.common.nonliterals.Context(session, context)
|
||||
|
||||
def parse_domains_and_contexts(self, elem, session):
|
||||
""" Converts domains and contexts from the view into Python objects,
|
||||
|
@ -998,10 +955,10 @@ class SearchView(View):
|
|||
@openerpweb.jsonrequest
|
||||
def save_filter(self, req, model, name, context_to_save, domain):
|
||||
Model = req.session.model("ir.filters")
|
||||
ctx = openerpweb.nonliterals.CompoundContext(context_to_save)
|
||||
ctx = web.common.nonliterals.CompoundContext(context_to_save)
|
||||
ctx.session = req.session
|
||||
ctx = ctx.evaluate()
|
||||
domain = openerpweb.nonliterals.CompoundDomain(domain)
|
||||
domain = web.common.nonliterals.CompoundDomain(domain)
|
||||
domain.session = req.session
|
||||
domain = domain.evaluate()
|
||||
uid = req.session._uid
|
||||
|
@ -1393,7 +1350,7 @@ class Reports(View):
|
|||
|
||||
report_srv = req.session.proxy("report")
|
||||
context = req.session.eval_context(
|
||||
openerpweb.nonliterals.CompoundContext(
|
||||
web.common.nonliterals.CompoundContext(
|
||||
req.context or {}, action[ "context"]))
|
||||
|
||||
report_data = {}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import time
|
||||
|
||||
import simplejson
|
||||
import web.common as openerpweb
|
||||
import web.common.http as openerpweb
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import web.common as openerpweb
|
||||
import web.common.http as openerpweb
|
||||
|
||||
WIDGET_CONTENT_PATTERN = """<!DOCTYPE html>
|
||||
<html>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import web.common as openerpweb
|
||||
import web.common.http as openerpweb
|
||||
from web.controllers.main import View
|
||||
|
||||
class DiagramView(View):
|
||||
|
|
|
@ -55,7 +55,7 @@ logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(
|
|||
help="Logging configuration file", metavar="FILE")
|
||||
optparser.add_option_group(logging_opts)
|
||||
|
||||
import web.common.dispatch
|
||||
import web.common.http
|
||||
|
||||
if __name__ == "__main__":
|
||||
(options, args) = optparser.parse_args(sys.argv[1:])
|
||||
|
@ -71,7 +71,7 @@ if __name__ == "__main__":
|
|||
else:
|
||||
logging.basicConfig(level=getattr(logging, options.log_level.upper()))
|
||||
|
||||
app = web.common.dispatch.Root(options)
|
||||
app = web.common.http.Root(options)
|
||||
|
||||
if options.proxy_mode:
|
||||
app = werkzeug.contrib.fixers.ProxyFix(app)
|
||||
|
|
Loading…
Reference in New Issue