merge upstream
bzr revid: chs@openerp.com-20110926112623-q68d3tebr80qt6l9
This commit is contained in:
commit
4261f1ad11
|
@ -2,27 +2,26 @@ import common
|
||||||
import controllers
|
import controllers
|
||||||
import common.dispatch
|
import common.dispatch
|
||||||
import logging
|
import logging
|
||||||
|
import optparse
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Options(object):
|
||||||
|
pass
|
||||||
|
|
||||||
def wsgi_postload():
|
def wsgi_postload():
|
||||||
import openerp.wsgi
|
import openerp.wsgi
|
||||||
|
import openerp.tools
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
_logger.info("embedded mode")
|
_logger.info("embedded mode")
|
||||||
class Options(object):
|
|
||||||
pass
|
|
||||||
o = Options()
|
o = Options()
|
||||||
o.dbfilter = '.*'
|
o.dbfilter = openerp.tools.config['dbfilter']
|
||||||
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions")
|
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions")
|
||||||
o.addons_path = os.path.dirname(os.path.dirname(__file__))
|
o.addons_path = os.path.dirname(os.path.dirname(__file__))
|
||||||
o.serve_static = True
|
o.serve_static = True
|
||||||
o.backend = 'local'
|
o.backend = 'local'
|
||||||
|
|
||||||
app = common.dispatch.Root(o)
|
app = common.dispatch.Root(o)
|
||||||
#import openerp.wsgi
|
|
||||||
openerp.wsgi.register_wsgi_handler(app)
|
openerp.wsgi.register_wsgi_handler(app)
|
||||||
|
|
||||||
# TODO
|
|
||||||
# if we detect that we are imported from the openerp server register common.Root() as a wsgi entry point
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js",
|
"static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js",
|
||||||
"static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js",
|
"static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js",
|
||||||
"static/lib/jquery.ui.notify/js/jquery.notify.js",
|
"static/lib/jquery.ui.notify/js/jquery.notify.js",
|
||||||
|
"static/lib/jquery.deferred-queue/jquery.deferred-queue.js",
|
||||||
"static/lib/json/json2.js",
|
"static/lib/json/json2.js",
|
||||||
"static/lib/qweb/qweb2.js",
|
"static/lib/qweb/qweb2.js",
|
||||||
"static/lib/underscore/underscore.js",
|
"static/lib/underscore/underscore.js",
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
"static/src/js/views.js",
|
"static/src/js/views.js",
|
||||||
"static/src/js/data.js",
|
"static/src/js/data.js",
|
||||||
"static/src/js/data_export.js",
|
"static/src/js/data_export.js",
|
||||||
|
"static/src/js/data_import.js",
|
||||||
"static/src/js/search.js",
|
"static/src/js/search.js",
|
||||||
"static/src/js/view_form.js",
|
"static/src/js/view_form.js",
|
||||||
"static/src/js/view_list.js",
|
"static/src/js/view_list.js",
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
"static/lib/jquery.ui.notify/css/ui.notify.css",
|
"static/lib/jquery.ui.notify/css/ui.notify.css",
|
||||||
"static/src/css/base.css",
|
"static/src/css/base.css",
|
||||||
"static/src/css/data_export.css",
|
"static/src/css/data_export.css",
|
||||||
|
"static/src/css/data_import.css",
|
||||||
],
|
],
|
||||||
'post_load' : 'wsgi_postload',
|
'post_load' : 'wsgi_postload',
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@ import werkzeug.wsgi
|
||||||
import ast
|
import ast
|
||||||
import nonliterals
|
import nonliterals
|
||||||
import http
|
import http
|
||||||
# import backendlocal as backend
|
import session
|
||||||
import session as backend
|
|
||||||
import openerplib
|
import openerplib
|
||||||
|
|
||||||
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
||||||
|
@ -79,12 +78,12 @@ class WebRequest(object):
|
||||||
|
|
||||||
.. attribute:: session_id
|
.. attribute:: session_id
|
||||||
|
|
||||||
opaque identifier for the :class:`backend.OpenERPSession` instance of
|
opaque identifier for the :class:`session.OpenERPSession` instance of
|
||||||
the current request
|
the current request
|
||||||
|
|
||||||
.. attribute:: session
|
.. attribute:: session
|
||||||
|
|
||||||
:class:`~backend.OpenERPSession` instance for the current request
|
:class:`~session.OpenERPSession` instance for the current request
|
||||||
|
|
||||||
.. attribute:: context
|
.. attribute:: context
|
||||||
|
|
||||||
|
@ -100,13 +99,12 @@ class WebRequest(object):
|
||||||
self.httpresponse = None
|
self.httpresponse = None
|
||||||
self.httpsession = request.session
|
self.httpsession = request.session
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def init(self, params):
|
def init(self, params):
|
||||||
self.params = dict(params)
|
self.params = dict(params)
|
||||||
# OpenERP session setup
|
# OpenERP session setup
|
||||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||||
self.session = self.httpsession.setdefault(
|
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession(self.config))
|
||||||
self.session_id, backend.OpenERPSession(
|
|
||||||
self.config.server_host, self.config.server_port))
|
|
||||||
self.context = self.params.pop('context', None)
|
self.context = self.params.pop('context', None)
|
||||||
self.debug = self.params.pop('debug', False) != False
|
self.debug = self.params.pop('debug', False) != False
|
||||||
|
|
||||||
|
@ -389,15 +387,14 @@ class Root(object):
|
||||||
for module in os.listdir(addons_path):
|
for module in os.listdir(addons_path):
|
||||||
if module not in addons_module:
|
if module not in addons_module:
|
||||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||||
if os.path.isfile(manifest_path):
|
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 = ast.literal_eval(open(manifest_path).read())
|
||||||
_logger.info("Loading %s", module)
|
_logger.info("Loading %s", module)
|
||||||
m = __import__(module)
|
m = __import__(module)
|
||||||
addons_module[module] = m
|
addons_module[module] = m
|
||||||
addons_manifest[module] = manifest
|
addons_manifest[module] = manifest
|
||||||
|
statics['/%s/static' % module] = path_static
|
||||||
statics['/%s/static' % module] = \
|
|
||||||
os.path.join(addons_path, module, 'static')
|
|
||||||
for k, v in controllers_class.items():
|
for k, v in controllers_class.items():
|
||||||
if k not in controllers_object:
|
if k not in controllers_object:
|
||||||
o = v()
|
o = v()
|
||||||
|
|
|
@ -20,6 +20,7 @@ def session(request, storage_path, session_cookie='sessionid'):
|
||||||
else:
|
else:
|
||||||
request.session = session_store.new()
|
request.session = session_store.new()
|
||||||
|
|
||||||
yield request.session
|
try:
|
||||||
|
yield request.session
|
||||||
session_store.save(request.session)
|
finally:
|
||||||
|
session_store.save(request.session)
|
||||||
|
|
|
@ -190,6 +190,34 @@ class NetRPCConnector(Connector):
|
||||||
socket.disconnect()
|
socket.disconnect()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
class LocalConnector(Connector):
|
||||||
|
"""
|
||||||
|
A type of connector that uses the XMLRPC protocol.
|
||||||
|
"""
|
||||||
|
PROTOCOL = 'local'
|
||||||
|
|
||||||
|
__logger = _getChildLogger(_logger, 'connector.local')
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send(self, service_name, method, *args):
|
||||||
|
import openerp
|
||||||
|
# TODO Exception handling
|
||||||
|
# This will be changed to be xmlrpc compatible
|
||||||
|
# OpenERPWarning code 1
|
||||||
|
# OpenERPException code 2
|
||||||
|
try:
|
||||||
|
result = openerp.netsvc.dispatch_rpc(service_name, method, args, None)
|
||||||
|
except openerp.netsvc.OpenERPDispatcherException, e:
|
||||||
|
fault = xmlrpclib.Fault(openerp.tools.exception_to_unicode(e.exception), e.traceback)
|
||||||
|
raise fault
|
||||||
|
except:
|
||||||
|
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||||
|
fault = xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value))
|
||||||
|
raise fault
|
||||||
|
return result
|
||||||
|
|
||||||
class Service(object):
|
class Service(object):
|
||||||
"""
|
"""
|
||||||
A class to execute RPC calls on a specific service of the remote server.
|
A class to execute RPC calls on a specific service of the remote server.
|
||||||
|
@ -363,7 +391,7 @@ class Model(object):
|
||||||
records = self.read(record_ids, fields or [], context or {})
|
records = self.read(record_ids, fields or [], context or {})
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def get_connector(hostname, protocol="xmlrpc", port="auto"):
|
def get_connector(hostname=None, protocol="xmlrpc", port="auto"):
|
||||||
"""
|
"""
|
||||||
A shortcut method to easily create a connector to a remote server using XMLRPC or NetRPC.
|
A shortcut method to easily create a connector to a remote server using XMLRPC or NetRPC.
|
||||||
|
|
||||||
|
@ -377,10 +405,12 @@ def get_connector(hostname, protocol="xmlrpc", port="auto"):
|
||||||
return XmlRPCConnector(hostname, port)
|
return XmlRPCConnector(hostname, port)
|
||||||
elif protocol == "netrpc":
|
elif protocol == "netrpc":
|
||||||
return NetRPCConnector(hostname, port)
|
return NetRPCConnector(hostname, port)
|
||||||
|
elif protocol == "local":
|
||||||
|
return LocalConnector()
|
||||||
else:
|
else:
|
||||||
raise ValueError("You must choose xmlrpc or netrpc")
|
raise ValueError("You must choose xmlrpc or netrpc or local")
|
||||||
|
|
||||||
def get_connection(hostname, protocol="xmlrpc", port='auto', database=None,
|
def get_connection(hostname=None, protocol="xmlrpc", port='auto', database=None,
|
||||||
login=None, password=None, user_id=None):
|
login=None, password=None, user_id=None):
|
||||||
"""
|
"""
|
||||||
A shortcut method to easily create a connection to a remote OpenERP server.
|
A shortcut method to easily create a connection to a remote OpenERP server.
|
||||||
|
|
|
@ -26,9 +26,8 @@ class OpenERPSession(object):
|
||||||
Used to store references to non-literal domains which need to be
|
Used to store references to non-literal domains which need to be
|
||||||
round-tripped to the client browser.
|
round-tripped to the client browser.
|
||||||
"""
|
"""
|
||||||
def __init__(self, server='127.0.0.1', port=8069):
|
def __init__(self, config):
|
||||||
self._server = server
|
self.config = config
|
||||||
self._port = port
|
|
||||||
self._db = False
|
self._db = False
|
||||||
self._uid = False
|
self._uid = False
|
||||||
self._login = False
|
self._login = False
|
||||||
|
@ -40,11 +39,16 @@ class OpenERPSession(object):
|
||||||
self._lang = {}
|
self._lang = {}
|
||||||
self.remote_timezone = 'utc'
|
self.remote_timezone = 'utc'
|
||||||
self.client_timezone = False
|
self.client_timezone = False
|
||||||
|
|
||||||
def build_connection(self):
|
def build_connection(self):
|
||||||
return openerplib.get_connection(hostname=self._server, port=self._port,
|
if self.config.backend == 'local':
|
||||||
database=self._db, login=self._login,
|
conn = openerplib.get_connection(protocol='local', database=self._db,
|
||||||
user_id=self._uid, password=self._password)
|
login=self._login, user_id=self._uid, password=self._password)
|
||||||
|
else:
|
||||||
|
conn = openerplib.get_connection(hostname=self.config.server_host,
|
||||||
|
port=self.config.server_port, database=self._db, login=self._login,
|
||||||
|
user_id=self._uid, password=self._password)
|
||||||
|
return conn
|
||||||
|
|
||||||
def proxy(self, service):
|
def proxy(self, service):
|
||||||
return self.build_connection().get_service(service)
|
return self.build_connection().get_service(service)
|
||||||
|
|
|
@ -172,14 +172,14 @@ class WebClient(openerpweb.Controller):
|
||||||
"grouping", "decimal_point", "thousands_sep"])
|
"grouping", "decimal_point", "thousands_sep"])
|
||||||
else:
|
else:
|
||||||
lang_obj = None
|
lang_obj = None
|
||||||
|
|
||||||
if lang.count("_") > 0:
|
if lang.count("_") > 0:
|
||||||
separator = "_"
|
separator = "_"
|
||||||
else:
|
else:
|
||||||
separator = "@"
|
separator = "@"
|
||||||
langs = lang.split(separator)
|
langs = lang.split(separator)
|
||||||
langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
|
langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
|
||||||
|
|
||||||
transs = {}
|
transs = {}
|
||||||
for addon_name in mods:
|
for addon_name in mods:
|
||||||
transl = {"messages":[]}
|
transl = {"messages":[]}
|
||||||
|
@ -246,7 +246,7 @@ class Database(openerpweb.Controller):
|
||||||
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)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return req.session.proxy("db").drop(password, db)
|
return req.session.proxy("db").drop(password, db)
|
||||||
except xmlrpclib.Fault, e:
|
except xmlrpclib.Fault, e:
|
||||||
|
@ -297,7 +297,7 @@ class Session(openerpweb.Controller):
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def login(self, req, db, login, password):
|
def login(self, req, db, login, password):
|
||||||
req.session.login(db, login, password)
|
req.session.login(db, login, password)
|
||||||
ctx = req.session.get_context()
|
ctx = req.session.get_context() if req.session._uid else {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"session_id": req.session_id,
|
"session_id": req.session_id,
|
||||||
|
@ -342,7 +342,7 @@ class Session(openerpweb.Controller):
|
||||||
}
|
}
|
||||||
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, req):
|
||||||
# TODO query server for installed web modules
|
# TODO query server for installed web modules
|
||||||
|
@ -721,7 +721,7 @@ class DataSet(openerpweb.Controller):
|
||||||
args[domain_id] = d
|
args[domain_id] = d
|
||||||
if context_id and len(args) - 1 >= context_id:
|
if context_id and len(args) - 1 >= context_id:
|
||||||
args[context_id] = c
|
args[context_id] = c
|
||||||
|
|
||||||
for i in xrange(len(args)):
|
for i in xrange(len(args)):
|
||||||
if isinstance(args[i], web.common.nonliterals.BaseContext):
|
if isinstance(args[i], web.common.nonliterals.BaseContext):
|
||||||
args[i] = req.session.eval_context(args[i])
|
args[i] = req.session.eval_context(args[i])
|
||||||
|
@ -969,7 +969,7 @@ class SearchView(View):
|
||||||
if field.get('context'):
|
if field.get('context'):
|
||||||
field["context"] = self.parse_domain(field["context"], req.session)
|
field["context"] = self.parse_domain(field["context"], req.session)
|
||||||
return {'fields': fields}
|
return {'fields': fields}
|
||||||
|
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def get_filters(self, req, model):
|
def get_filters(self, req, model):
|
||||||
Model = req.session.model("ir.filters")
|
Model = req.session.model("ir.filters")
|
||||||
|
@ -978,7 +978,7 @@ class SearchView(View):
|
||||||
filter["context"] = req.session.eval_context(self.parse_context(filter["context"], req.session))
|
filter["context"] = req.session.eval_context(self.parse_context(filter["context"], req.session))
|
||||||
filter["domain"] = req.session.eval_domain(self.parse_domain(filter["domain"], req.session))
|
filter["domain"] = req.session.eval_domain(self.parse_domain(filter["domain"], req.session))
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def save_filter(self, req, model, name, context_to_save, domain):
|
def save_filter(self, req, model, name, context_to_save, domain):
|
||||||
Model = req.session.model("ir.filters")
|
Model = req.session.model("ir.filters")
|
||||||
|
@ -1388,7 +1388,7 @@ class Reports(View):
|
||||||
report_data['form'] = action['datas']['form']
|
report_data['form'] = action['datas']['form']
|
||||||
if 'ids' in action['datas']:
|
if 'ids' in action['datas']:
|
||||||
report_ids = action['datas']['ids']
|
report_ids = action['datas']['ids']
|
||||||
|
|
||||||
report_id = report_srv.report(
|
report_id = report_srv.report(
|
||||||
req.session._db, req.session._uid, req.session._password,
|
req.session._db, req.session._uid, req.session._password,
|
||||||
action["report_name"], report_ids,
|
action["report_name"], report_ids,
|
||||||
|
@ -1400,6 +1400,7 @@ class Reports(View):
|
||||||
req.session._db, req.session._uid, req.session._password, report_id)
|
req.session._db, req.session._uid, req.session._password, report_id)
|
||||||
if report_struct["state"]:
|
if report_struct["state"]:
|
||||||
break
|
break
|
||||||
|
|
||||||
time.sleep(self.POLLING_DELAY)
|
time.sleep(self.POLLING_DELAY)
|
||||||
|
|
||||||
report = base64.b64decode(report_struct['result'])
|
report = base64.b64decode(report_struct['result'])
|
||||||
|
@ -1413,3 +1414,108 @@ class Reports(View):
|
||||||
('Content-Type', report_mimetype),
|
('Content-Type', report_mimetype),
|
||||||
('Content-Length', len(report))],
|
('Content-Length', len(report))],
|
||||||
cookies={'fileToken': int(token)})
|
cookies={'fileToken': int(token)})
|
||||||
|
|
||||||
|
|
||||||
|
class Import(View):
|
||||||
|
_cp_path = "/web/import"
|
||||||
|
|
||||||
|
def fields_get(self, req, model):
|
||||||
|
Model = req.session.model(model)
|
||||||
|
fields = Model.fields_get(False, req.session.eval_context(req.context))
|
||||||
|
return fields
|
||||||
|
|
||||||
|
@openerpweb.httprequest
|
||||||
|
def detect_data(self, req, csvfile, csvsep, csvdel, csvcode, jsonp):
|
||||||
|
try:
|
||||||
|
data = list(csv.reader(
|
||||||
|
csvfile, quotechar=str(csvdel), delimiter=str(csvsep)))
|
||||||
|
except csv.Error, e:
|
||||||
|
csvfile.seek(0)
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'error': {
|
||||||
|
'message': 'Error parsing CSV file: %s' % e,
|
||||||
|
# decodes each byte to a unicode character, which may or
|
||||||
|
# may not be printable, but decoding will succeed.
|
||||||
|
# Otherwise simplejson will try to decode the `str` using
|
||||||
|
# utf-8, which is very likely to blow up on characters out
|
||||||
|
# of the ascii range (in range [128, 256))
|
||||||
|
'preview': csvfile.read(200).decode('iso-8859-1')}}))
|
||||||
|
|
||||||
|
try:
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps(
|
||||||
|
{'records': data[:10]}, encoding=csvcode))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({
|
||||||
|
'message': u"Failed to decode CSV file using encoding %s, "
|
||||||
|
u"try switching to a different encoding" % csvcode
|
||||||
|
}))
|
||||||
|
|
||||||
|
@openerpweb.httprequest
|
||||||
|
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp,
|
||||||
|
meta):
|
||||||
|
modle_obj = req.session.model(model)
|
||||||
|
skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')(
|
||||||
|
simplejson.loads(meta))
|
||||||
|
|
||||||
|
error = None
|
||||||
|
if not (csvdel and len(csvdel) == 1):
|
||||||
|
error = u"The CSV delimiter must be a single character"
|
||||||
|
|
||||||
|
if not indices and fields:
|
||||||
|
error = u"You must select at least one field to import"
|
||||||
|
|
||||||
|
if error:
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'error': {'message': error}}))
|
||||||
|
|
||||||
|
# skip ignored records
|
||||||
|
data_record = itertools.islice(
|
||||||
|
csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)),
|
||||||
|
skip, None)
|
||||||
|
|
||||||
|
# if only one index, itemgetter will return an atom rather than a tuple
|
||||||
|
if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
|
||||||
|
else: mapper = operator.itemgetter(*indices)
|
||||||
|
|
||||||
|
data = None
|
||||||
|
error = None
|
||||||
|
try:
|
||||||
|
# decode each data row
|
||||||
|
data = [
|
||||||
|
[record.decode(csvcode) for record in row]
|
||||||
|
for row in itertools.imap(mapper, data_record)
|
||||||
|
# don't insert completely empty rows (can happen due to fields
|
||||||
|
# filtering in case of e.g. o2m content rows)
|
||||||
|
if any(row)
|
||||||
|
]
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
error = u"Failed to decode CSV file using encoding %s" % csvcode
|
||||||
|
except csv.Error, e:
|
||||||
|
error = u"Could not process CSV file: %s" % e
|
||||||
|
|
||||||
|
# If the file contains nothing,
|
||||||
|
if not data:
|
||||||
|
error = u"File to import is empty"
|
||||||
|
if error:
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'error': {'message': error}}))
|
||||||
|
|
||||||
|
try:
|
||||||
|
(code, record, message, _nope) = modle_obj.import_data(
|
||||||
|
fields, data, 'init', '', False,
|
||||||
|
req.session.eval_context(req.context))
|
||||||
|
except xmlrpclib.Fault, e:
|
||||||
|
error = {"message": u"%s, %s" % (e.faultCode, e.faultString)}
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'error':error}))
|
||||||
|
|
||||||
|
if code != -1:
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'success':True}))
|
||||||
|
|
||||||
|
msg = u"Error during import: %s\n\nTrying to import record %r" % (
|
||||||
|
message, record)
|
||||||
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
|
jsonp, simplejson.dumps({'error': {'message':msg}}))
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
Copyright 2011 Xavier Morel. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY XAVIER MOREL ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,59 @@
|
||||||
|
.. -*- restructuredtext -*-
|
||||||
|
|
||||||
|
In jQuery 1.5, jQuery has introduced a Deferred object in order to
|
||||||
|
better handle callbacks.
|
||||||
|
|
||||||
|
Along with Deferred, it introduced ``jQuery.when`` which allows, among
|
||||||
|
other things, for multiplexing deferreds (waiting on multiple
|
||||||
|
deferreds at the same time).
|
||||||
|
|
||||||
|
While this is very nice if all deferreds are available at the same
|
||||||
|
point, it doesn't really work if the resolution of a deferred can
|
||||||
|
generate more deferreds, or for collections of deferreds coming from
|
||||||
|
multiple sources (which may not be aware of one another).
|
||||||
|
|
||||||
|
Deferred.queue tries to be a solution to this. It is based on the
|
||||||
|
principle of FIFO multiple-producers multiple-consumers tasks queues,
|
||||||
|
such as Python's `queue.Queue`_. Any code with a reference to the
|
||||||
|
queue can add a promise to the queue, and the queue (which is a
|
||||||
|
promise itself) will only be resolved once all promises within are
|
||||||
|
resolved.
|
||||||
|
|
||||||
|
Quickstart
|
||||||
|
----------
|
||||||
|
|
||||||
|
Deferred.queue has a very simple life cycle: it is dormant when
|
||||||
|
created, it starts working as soon as promises get added to it (via
|
||||||
|
``.push``, or by providing them directly to its constructor), and as
|
||||||
|
soon as the last promise in the queue is resolved it resolves itself.
|
||||||
|
|
||||||
|
If any promise in the queue fails, the queue itself will be rejected
|
||||||
|
(without waiting for further promises).
|
||||||
|
|
||||||
|
Once a queue has been resolved or rejected, adding new promises to it
|
||||||
|
results in an error.
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
``jQuery.Deferred.queue([promises...])``
|
||||||
|
Creates a new deferred queue. Can be primed with a series of promise
|
||||||
|
objects.
|
||||||
|
|
||||||
|
``.push(promise...)``
|
||||||
|
Adds promises to the queue. Returns the queue itself (can be
|
||||||
|
chained).
|
||||||
|
|
||||||
|
If the queue has already been resolved or rejected, raises an error.
|
||||||
|
|
||||||
|
``.then([doneCallbacks][, failCallbacks])``
|
||||||
|
Promise/A ``then`` method.
|
||||||
|
|
||||||
|
``.done(doneCallbacks)``
|
||||||
|
jQuery ``done`` extension to promise objects
|
||||||
|
|
||||||
|
``.fail(failCallbacks)``
|
||||||
|
jQuery ``fail`` extension to promise objects
|
||||||
|
|
||||||
|
.. _queue.Queue:
|
||||||
|
http://docs.python.org/dev/library/queue.html
|
|
@ -0,0 +1,34 @@
|
||||||
|
(function ($) {
|
||||||
|
"use strict";
|
||||||
|
$.extend($.Deferred, {
|
||||||
|
queue: function () {
|
||||||
|
var queueDeferred = $.Deferred();
|
||||||
|
var promises = 0;
|
||||||
|
|
||||||
|
function resolve() {
|
||||||
|
if (--promises > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout($.proxy(queueDeferred, 'resolve'), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var promise = $.extend(queueDeferred.promise(), {
|
||||||
|
push: function () {
|
||||||
|
if (this.isResolved() || this.isRejected()) {
|
||||||
|
throw new Error("Can not add promises to a resolved "
|
||||||
|
+ "or rejected promise queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
promises += 1;
|
||||||
|
$.when.apply(null, arguments).then(
|
||||||
|
resolve, $.proxy(queueDeferred, 'reject'));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (arguments.length) {
|
||||||
|
promise.push.apply(promise, arguments);
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(jQuery)
|
|
@ -892,6 +892,9 @@ label.error {
|
||||||
.openerp .oe_forms input.field_datetime {
|
.openerp .oe_forms input.field_datetime {
|
||||||
min-width: 11em;
|
min-width: 11em;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_forms.oe_frame .oe_datepicker_root {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.openerp .oe_forms .button {
|
.openerp .oe_forms .button {
|
||||||
color: #4c4c4c;
|
color: #4c4c4c;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -904,7 +907,14 @@ label.error {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
top: 5px;
|
top: 3px;
|
||||||
|
}
|
||||||
|
.openerp .oe_datepicker_root {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.openerp .oe_datepicker_root input[type="text"] {
|
||||||
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
.openerp .oe_input_icon_disabled {
|
.openerp .oe_input_icon_disabled {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
.openerp .oe_import_grid {
|
||||||
|
border: none;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.openerp .oe_import_grid-header .oe_import_grid-cell {
|
||||||
|
background: url(../img/gradientlinebg.gif) repeat-x #CCCCCC;
|
||||||
|
border-bottom: 1px solid #E3E3E3;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.openerp .oe_import_grid-row .oe_import_grid-cell {
|
||||||
|
border-bottom: 1px solid #E3E3E3;
|
||||||
|
}
|
||||||
|
.openerp .separator.horizontal {
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
margin: 6px 4px 6px 1px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
.openerp .duplicate_fld{
|
||||||
|
background-color:#FF6666;
|
||||||
|
}
|
||||||
|
.openerp .select_fld{
|
||||||
|
background: none repeat scroll 0 0 white;
|
||||||
|
}
|
||||||
|
.openerp .ui-autocomplete {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 182 B |
|
@ -58,7 +58,7 @@ openerp.web = function(instance) {
|
||||||
openerp.web.formats(instance);
|
openerp.web.formats(instance);
|
||||||
openerp.web.chrome(instance);
|
openerp.web.chrome(instance);
|
||||||
openerp.web.data(instance);
|
openerp.web.data(instance);
|
||||||
var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export"];
|
var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"];
|
||||||
for(var i=0; i<files.length; i++) {
|
for(var i=0; i<files.length; i++) {
|
||||||
if(openerp.web[files[i]]) {
|
if(openerp.web[files[i]]) {
|
||||||
openerp.web[files[i]](instance);
|
openerp.web[files[i]](instance);
|
||||||
|
|
|
@ -218,9 +218,8 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||||
this.$element.closest(".openerp")
|
this.$element.closest(".openerp")
|
||||||
.removeClass("login-mode")
|
.removeClass("login-mode")
|
||||||
.addClass("database_block");
|
.addClass("database_block");
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
|
var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
|
||||||
self.db_list = result.db_list;
|
self.db_list = result.db_list;
|
||||||
});
|
});
|
||||||
|
@ -232,7 +231,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||||
self.lang_list = result.lang_list;
|
self.lang_list = result.lang_list;
|
||||||
});
|
});
|
||||||
$.when(fetch_db, fetch_langs).then(function () {self.do_create();});
|
$.when(fetch_db, fetch_langs).then(function () {self.do_create();});
|
||||||
|
|
||||||
this.$element.find('#db-create').click(this.do_create);
|
this.$element.find('#db-create').click(this.do_create);
|
||||||
this.$element.find('#db-drop').click(this.do_drop);
|
this.$element.find('#db-drop').click(this.do_drop);
|
||||||
this.$element.find('#db-backup').click(this.do_backup);
|
this.$element.find('#db-backup').click(this.do_backup);
|
||||||
|
@ -399,7 +398,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||||
do_restore: function() {
|
do_restore: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.$option_id.html(QWeb.render("RestoreDB", self));
|
self.$option_id.html(QWeb.render("RestoreDB", self));
|
||||||
|
|
||||||
self.$option_id.find("form[name=restore_db_form]").validate({
|
self.$option_id.find("form[name=restore_db_form]").validate({
|
||||||
submitHandler: function (form) {
|
submitHandler: function (form) {
|
||||||
$.blockUI({message:'<img src="/web/static/src/img/throbber2.gif">'});
|
$.blockUI({message:'<img src="/web/static/src/img/throbber2.gif">'});
|
||||||
|
@ -467,10 +466,11 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||||
/**
|
/**
|
||||||
* @constructs openerp.web.Login
|
* @constructs openerp.web.Login
|
||||||
* @extends openerp.web.Widget
|
* @extends openerp.web.Widget
|
||||||
*
|
*
|
||||||
* @param parent
|
* @param parent
|
||||||
* @param element_id
|
* @param element_id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init: function(parent, element_id) {
|
init: function(parent, element_id) {
|
||||||
this._super(parent, element_id);
|
this._super(parent, element_id);
|
||||||
this.has_local_storage = typeof(localStorage) != 'undefined';
|
this.has_local_storage = typeof(localStorage) != 'undefined';
|
||||||
|
@ -496,7 +496,7 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||||
},
|
},
|
||||||
display: function() {
|
display: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.$element.html(QWeb.render("Login", this));
|
this.$element.html(QWeb.render("Login", this));
|
||||||
this.database = new openerp.web.Database(
|
this.database = new openerp.web.Database(
|
||||||
this, "oe_database", "oe_db_options");
|
this, "oe_database", "oe_db_options");
|
||||||
|
@ -574,14 +574,13 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||||
/**
|
/**
|
||||||
* @constructs openerp.web.Header
|
* @constructs openerp.web.Header
|
||||||
* @extends openerp.web.Widget
|
* @extends openerp.web.Widget
|
||||||
*
|
*
|
||||||
* @param parent
|
* @param parent
|
||||||
*/
|
*/
|
||||||
init: function(parent) {
|
init: function(parent) {
|
||||||
this._super(parent);
|
this._super(parent);
|
||||||
this.qs = "?" + jQuery.param.querystring();
|
this.qs = "?" + jQuery.param.querystring();
|
||||||
this.$content = $();
|
this.$content = $();
|
||||||
console.debug("initializing header with id", this.element_id);
|
|
||||||
this.update_promise = $.Deferred().resolve();
|
this.update_promise = $.Deferred().resolve();
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
|
@ -666,7 +665,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
on_action: function(action) {
|
on_action: function(action) {
|
||||||
},
|
},
|
||||||
on_preferences: function(){
|
on_preferences: function(){
|
||||||
|
@ -702,10 +701,11 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||||
},
|
},
|
||||||
Save: function(){
|
Save: function(){
|
||||||
var inner_viewmanager = action_manager.inner_viewmanager;
|
var inner_viewmanager = action_manager.inner_viewmanager;
|
||||||
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save(function(){
|
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
|
||||||
inner_viewmanager.start();
|
.then(function() {
|
||||||
|
self.dialog.stop();
|
||||||
|
window.location.reload();
|
||||||
});
|
});
|
||||||
$(this).dialog('destroy')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -713,7 +713,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||||
action_manager.appendTo(this.dialog);
|
action_manager.appendTo(this.dialog);
|
||||||
action_manager.render(this.dialog);
|
action_manager.render(this.dialog);
|
||||||
},
|
},
|
||||||
|
|
||||||
change_password :function() {
|
change_password :function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.dialog = new openerp.web.Dialog(this,{
|
this.dialog = new openerp.web.Dialog(this,{
|
||||||
|
@ -728,21 +728,13 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||||
submitHandler: function (form) {
|
submitHandler: function (form) {
|
||||||
self.rpc("/web/session/change_password",{
|
self.rpc("/web/session/change_password",{
|
||||||
'fields': $(form).serializeArray()
|
'fields': $(form).serializeArray()
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
self.display_error(result);
|
self.display_error(result);
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else {
|
self.session.logout();
|
||||||
if (result.new_password) {
|
}
|
||||||
self.session.password = result.new_password;
|
|
||||||
var session = new openerp.web.Session(self.session.server, self.session.port);
|
|
||||||
session.start();
|
|
||||||
session.session_login(self.session.db, self.session.login, self.session.password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.notification.notify("Changed Password", "Password has been changed successfully");
|
|
||||||
self.dialog.close();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -766,7 +758,7 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
|
||||||
/**
|
/**
|
||||||
* @constructs openerp.web.Menu
|
* @constructs openerp.web.Menu
|
||||||
* @extends openerp.web.Widget
|
* @extends openerp.web.Widget
|
||||||
*
|
*
|
||||||
* @param parent
|
* @param parent
|
||||||
* @param element_id
|
* @param element_id
|
||||||
* @param secondary_menu_id
|
* @param secondary_menu_id
|
||||||
|
@ -918,7 +910,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
/**
|
/**
|
||||||
* @constructs openerp.web.WebClient
|
* @constructs openerp.web.WebClient
|
||||||
* @extends openerp.web.Widget
|
* @extends openerp.web.Widget
|
||||||
*
|
*
|
||||||
* @param element_id
|
* @param element_id
|
||||||
*/
|
*/
|
||||||
init: function(element_id) {
|
init: function(element_id) {
|
||||||
|
@ -963,7 +955,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
this.session.start();
|
this.session.start();
|
||||||
this.login.start();
|
this.login.start();
|
||||||
this.menu.start();
|
this.menu.start();
|
||||||
console.debug("The openerp client has been initialized.");
|
|
||||||
},
|
},
|
||||||
on_logged: function() {
|
on_logged: function() {
|
||||||
if(this.action_manager)
|
if(this.action_manager)
|
||||||
|
@ -1009,7 +1000,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
self.execute_home_action(home_action[0], ds);
|
self.execute_home_action(home_action[0], ds);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
default_home: function () {
|
default_home: function () {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Bundles the execution of the home action
|
* Bundles the execution of the home action
|
||||||
|
@ -1034,7 +1025,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
},
|
},
|
||||||
do_url_set_hash: function(url) {
|
do_url_set_hash: function(url) {
|
||||||
if(!this.url_external_hashchange) {
|
if(!this.url_external_hashchange) {
|
||||||
console.log("url set #hash to",url);
|
|
||||||
this.url_internal_hashchange = true;
|
this.url_internal_hashchange = true;
|
||||||
jQuery.bbq.pushState(url);
|
jQuery.bbq.pushState(url);
|
||||||
}
|
}
|
||||||
|
@ -1042,10 +1032,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||||
on_url_hashchange: function() {
|
on_url_hashchange: function() {
|
||||||
if(this.url_internal_hashchange) {
|
if(this.url_internal_hashchange) {
|
||||||
this.url_internal_hashchange = false;
|
this.url_internal_hashchange = false;
|
||||||
console.log("url jump to FLAG OFF");
|
|
||||||
} else {
|
} else {
|
||||||
var url = jQuery.deparam.fragment();
|
var url = jQuery.deparam.fragment();
|
||||||
console.log("url jump to",url);
|
|
||||||
this.url_external_hashchange = true;
|
this.url_external_hashchange = true;
|
||||||
this.action_manager.on_url_hashchange(url);
|
this.action_manager.on_url_hashchange(url);
|
||||||
this.url_external_hashchange = false;
|
this.url_external_hashchange = false;
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
/*---------------------------------------------------------
|
/*---------------------------------------------------------
|
||||||
* OpenERP Web core
|
* OpenERP Web core
|
||||||
*--------------------------------------------------------*/
|
*--------------------------------------------------------*/
|
||||||
|
var console;
|
||||||
|
if (!console) {
|
||||||
|
console = {log: function () {}};
|
||||||
|
}
|
||||||
if (!console.debug) {
|
if (!console.debug) {
|
||||||
console.debug = console.log;
|
console.debug = console.log;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +486,8 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web
|
||||||
self.user_context = result.context;
|
self.user_context = result.context;
|
||||||
self.db = result.db;
|
self.db = result.db;
|
||||||
self.session_save();
|
self.session_save();
|
||||||
self.on_session_valid();
|
if (self.uid)
|
||||||
|
self.on_session_valid();
|
||||||
return true;
|
return true;
|
||||||
}).then(success_callback);
|
}).then(success_callback);
|
||||||
},
|
},
|
||||||
|
@ -771,7 +775,6 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp
|
||||||
* // stuff that you want to init before the rendering
|
* // stuff that you want to init before the rendering
|
||||||
* },
|
* },
|
||||||
* start: function() {
|
* start: function() {
|
||||||
* this._super();
|
|
||||||
* // stuff you want to make after the rendering, `this.$element` holds a correct value
|
* // stuff you want to make after the rendering, `this.$element` holds a correct value
|
||||||
* this.$element.find(".my_button").click(/* an example of event binding * /);
|
* this.$element.find(".my_button").click(/* an example of event binding * /);
|
||||||
*
|
*
|
||||||
|
@ -915,6 +918,8 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||||
* @returns {jQuery.Deferred}
|
* @returns {jQuery.Deferred}
|
||||||
*/
|
*/
|
||||||
start: function() {
|
start: function() {
|
||||||
|
/* The default implementation is only useful for retro-compatibility, it is
|
||||||
|
not necessary to call it using _super() when using Widget for new components. */
|
||||||
if (!this.$element) {
|
if (!this.$element) {
|
||||||
var tmp = document.getElementById(this.element_id);
|
var tmp = document.getElementById(this.element_id);
|
||||||
this.$element = tmp ? $(tmp) : undefined;
|
this.$element = tmp ? $(tmp) : undefined;
|
||||||
|
@ -922,7 +927,7 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||||
return $.Deferred().done().promise();
|
return $.Deferred().done().promise();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Destroys the current widget, also destory all its children before destroying itself.
|
* Destroys the current widget, also destroy all its children before destroying itself.
|
||||||
*/
|
*/
|
||||||
stop: function() {
|
stop: function() {
|
||||||
_.each(_.clone(this.widget_children), function(el) {
|
_.each(_.clone(this.widget_children), function(el) {
|
||||||
|
@ -943,7 +948,6 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||||
* If that's not the case this method will simply return `false`.
|
* If that's not the case this method will simply return `false`.
|
||||||
*/
|
*/
|
||||||
do_action: function(action, on_finished) {
|
do_action: function(action, on_finished) {
|
||||||
console.log('Widget.do_action', action, on_finished);
|
|
||||||
if (this.widget_parent) {
|
if (this.widget_parent) {
|
||||||
return this.widget_parent.do_action(action, on_finished);
|
return this.widget_parent.do_action(action, on_finished);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
openerp.web.data_import = function(openerp) {
|
||||||
|
var QWeb = openerp.web.qweb;
|
||||||
|
/**
|
||||||
|
* Safari does not deal well at all with raw JSON data being returned. As a
|
||||||
|
* result, we're going to cheat by using a pseudo-jsonp: instead of getting
|
||||||
|
* JSON data in the iframe, we're getting a ``script`` tag which consists of a
|
||||||
|
* function call and the returned data (the json dump).
|
||||||
|
*
|
||||||
|
* The function is an auto-generated name bound to ``window``, which calls
|
||||||
|
* back into the callback provided here.
|
||||||
|
*
|
||||||
|
* @param {Object} form the form element (DOM or jQuery) to use in the call
|
||||||
|
* @param {Object} attributes jquery.form attributes object
|
||||||
|
* @param {Function} callback function to call with the returned data
|
||||||
|
*/
|
||||||
|
function jsonp(form, attributes, callback) {
|
||||||
|
attributes = attributes || {};
|
||||||
|
var options = {jsonp: _.uniqueId('import_callback_')};
|
||||||
|
window[options.jsonp] = function () {
|
||||||
|
delete window[options.jsonp];
|
||||||
|
callback.apply(null, arguments);
|
||||||
|
};
|
||||||
|
if ('data' in attributes) {
|
||||||
|
_.extend(attributes.data, options);
|
||||||
|
} else {
|
||||||
|
_.extend(attributes, {data: options});
|
||||||
|
}
|
||||||
|
$(form).ajaxSubmit(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||||
|
template: 'ImportDataView',
|
||||||
|
dialog_title: "Import Data",
|
||||||
|
init: function(parent, dataset){
|
||||||
|
var self = this;
|
||||||
|
this._super(parent, {});
|
||||||
|
this.model = parent.model;
|
||||||
|
this.fields = [];
|
||||||
|
this.all_fields = [];
|
||||||
|
this.required_fields = null;
|
||||||
|
|
||||||
|
var convert_fields = function (root, prefix) {
|
||||||
|
prefix = prefix || '';
|
||||||
|
_(root.fields).each(function (f) {
|
||||||
|
self.all_fields.push(prefix + f.name);
|
||||||
|
if (f.fields) {
|
||||||
|
convert_fields(f, prefix + f.id + '/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.ready = $.Deferred.queue().then(function () {
|
||||||
|
self.required_fields = _(self.fields).chain()
|
||||||
|
.filter(function (field) { return field.required; })
|
||||||
|
.pluck('name')
|
||||||
|
.value();
|
||||||
|
convert_fields(self);
|
||||||
|
self.all_fields.sort();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
start: function() {
|
||||||
|
var self = this;
|
||||||
|
this._super();
|
||||||
|
this.open({
|
||||||
|
modal: true,
|
||||||
|
width: '70%',
|
||||||
|
height: 'auto',
|
||||||
|
position: 'top',
|
||||||
|
buttons: [
|
||||||
|
{text: "Close", click: function() { self.stop(); }},
|
||||||
|
{text: "Import File", click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
|
||||||
|
],
|
||||||
|
close: function(event, ui) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.toggle_import_button(false);
|
||||||
|
this.$element.find('#csvfile').change(this.on_autodetect_data);
|
||||||
|
this.$element.find('fieldset').change(this.on_autodetect_data);
|
||||||
|
this.$element.find('fieldset legend').click(function() {
|
||||||
|
$(this).next().toggle();
|
||||||
|
});
|
||||||
|
this.ready.push(new openerp.web.DataSet(this, this.model).call(
|
||||||
|
'fields_get', [], function (fields) {
|
||||||
|
self.graft_fields(fields);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
graft_fields: function (fields, parent, level) {
|
||||||
|
parent = parent || this;
|
||||||
|
level = level || 0;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
_(fields).each(function (field, field_name) {
|
||||||
|
var f = {
|
||||||
|
id: field_name,
|
||||||
|
name: field_name,
|
||||||
|
string: field.string,
|
||||||
|
required: field.required
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (field.type) {
|
||||||
|
case 'many2many':
|
||||||
|
case 'many2one':
|
||||||
|
f.name += '/id';
|
||||||
|
break;
|
||||||
|
case 'one2many':
|
||||||
|
f.name += '/id';
|
||||||
|
f.fields = [];
|
||||||
|
// only fetch sub-fields to a depth of 2 levels
|
||||||
|
if (level < 2) {
|
||||||
|
self.ready.push(new openerp.web.DataSet(self, field.relation).call(
|
||||||
|
'fields_get', [], function (fields) {
|
||||||
|
self.graft_fields(fields, f, level+1);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent.fields.push(f);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggle_import_button: function (newstate) {
|
||||||
|
this.$dialog.dialog('widget')
|
||||||
|
.find('.oe-dialog-import-button')
|
||||||
|
.button('option', 'disabled', !newstate);
|
||||||
|
},
|
||||||
|
do_import: function() {
|
||||||
|
if(!this.$element.find('#csvfile').val()) { return; }
|
||||||
|
var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
|
||||||
|
var with_headers = this.$element.find('#file_has_headers').prop('checked');
|
||||||
|
if (!lines_to_skip && with_headers) {
|
||||||
|
lines_to_skip = 1;
|
||||||
|
}
|
||||||
|
var indices = [], fields = [];
|
||||||
|
this.$element.find(".sel_fields").each(function(index, element) {
|
||||||
|
var val = element.value;
|
||||||
|
if (!val) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
indices.push(index);
|
||||||
|
fields.push(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
jsonp(this.$element.find('#import_data'), {
|
||||||
|
url: '/web/import/import_data',
|
||||||
|
data: {
|
||||||
|
model: this.model,
|
||||||
|
meta: JSON.stringify({
|
||||||
|
skip: lines_to_skip,
|
||||||
|
indices: indices,
|
||||||
|
fields: fields
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, this.on_import_results);
|
||||||
|
},
|
||||||
|
on_autodetect_data: function() {
|
||||||
|
if(!this.$element.find('#csvfile').val()) { return; }
|
||||||
|
jsonp(this.$element.find('#import_data'), {
|
||||||
|
url: '/web/import/detect_data'
|
||||||
|
}, this.on_import_results);
|
||||||
|
},
|
||||||
|
on_import_results: function(results) {
|
||||||
|
this.$element.find('#result').empty();
|
||||||
|
var headers, result_node = this.$element.find("#result");
|
||||||
|
|
||||||
|
if (results['records']) {
|
||||||
|
var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
|
||||||
|
with_headers = this.$element.find('#file_has_headers').prop('checked');
|
||||||
|
headers = with_headers ? results.records[0] : null;
|
||||||
|
|
||||||
|
result_node.append(QWeb.render('ImportView.result', {
|
||||||
|
'headers': headers,
|
||||||
|
'records': lines_to_skip ? results.records.slice(lines_to_skip)
|
||||||
|
: with_headers ? results.records.slice(1)
|
||||||
|
: results.records
|
||||||
|
}));
|
||||||
|
} else if (results['error']) {
|
||||||
|
result_node.append(QWeb.render('ImportView.error', {
|
||||||
|
'error': results['error']}));
|
||||||
|
} else if (results['success']) {
|
||||||
|
if (this.widget_parent.widget_parent.active_view == "list") {
|
||||||
|
this.widget_parent.reload_content();
|
||||||
|
}
|
||||||
|
this.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.ready.then(function () {
|
||||||
|
var $fields = self.$element.find('.sel_fields').bind('blur', function () {
|
||||||
|
if (this.value && !_(self.all_fields).contains(this.value)) {
|
||||||
|
this.value = '';
|
||||||
|
}
|
||||||
|
}).autocomplete({
|
||||||
|
minLength: 0,
|
||||||
|
source: self.all_fields,
|
||||||
|
change: self.on_check_field_values
|
||||||
|
}).focus(function () {
|
||||||
|
$(this).autocomplete('search');
|
||||||
|
});
|
||||||
|
// Column auto-detection
|
||||||
|
_(headers).each(function (header, index) {
|
||||||
|
var f =_(self.fields).detect(function (field) {
|
||||||
|
// TODO: levenshtein between header and field.string
|
||||||
|
return field.name === header || field.string.toLowerCase() === header;
|
||||||
|
});
|
||||||
|
if (f) {
|
||||||
|
$fields.eq(index).val(f.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.on_check_field_values();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Looks through all the field selections, and tries to find if two
|
||||||
|
* (or more) columns were matched to the same model field.
|
||||||
|
*
|
||||||
|
* Returns a map of the multiply-mapped fields to an array of offending
|
||||||
|
* columns (not actually columns, but the inputs containing the same field
|
||||||
|
* names).
|
||||||
|
*
|
||||||
|
* Also has the side-effect of marking the discovered inputs with the class
|
||||||
|
* ``duplicate_fld``.
|
||||||
|
*
|
||||||
|
* @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
|
||||||
|
*/
|
||||||
|
find_duplicate_fields: function() {
|
||||||
|
// Maps values to DOM nodes, in order to discover duplicates
|
||||||
|
var values = {}, duplicates = {};
|
||||||
|
this.$element.find(".sel_fields").each(function(index, element) {
|
||||||
|
var value = element.value;
|
||||||
|
var $element = $(element).removeClass('duplicate_fld');
|
||||||
|
if (!value) { return; }
|
||||||
|
|
||||||
|
if (!(value in values)) {
|
||||||
|
values[value] = element;
|
||||||
|
} else {
|
||||||
|
var same_valued_field = values[value];
|
||||||
|
if (value in duplicates) {
|
||||||
|
duplicates[value].push(element);
|
||||||
|
} else {
|
||||||
|
duplicates[value] = [same_valued_field, element];
|
||||||
|
}
|
||||||
|
$element.add(same_valued_field).addClass('duplicate_fld');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return duplicates;
|
||||||
|
},
|
||||||
|
on_check_field_values: function () {
|
||||||
|
this.$element.find("#message, #msg").remove();
|
||||||
|
|
||||||
|
var required_valid = this.check_required();
|
||||||
|
|
||||||
|
var duplicates = this.find_duplicate_fields();
|
||||||
|
if (_.isEmpty(duplicates)) {
|
||||||
|
this.toggle_import_button(required_valid);
|
||||||
|
} else {
|
||||||
|
var $err = $('<div id="msg" style="color: red;">Destination fields should only be selected once, some fields are selected more than once:</div>').insertBefore(this.$element.find('#result'));
|
||||||
|
var $dupes = $('<dl>').appendTo($err);
|
||||||
|
_(duplicates).each(function(elements, value) {
|
||||||
|
$('<dt>').text(value).appendTo($dupes);
|
||||||
|
_(elements).each(function(element) {
|
||||||
|
var cell = $(element).closest('td');
|
||||||
|
$('<dd>').text(cell.parent().children().index(cell)).appendTo($dupes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.toggle_import_button(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
check_required: function() {
|
||||||
|
if (!this.required_fields.length) { return true; }
|
||||||
|
|
||||||
|
var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
|
||||||
|
.pluck('value')
|
||||||
|
.compact()
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var missing_fields = _.difference(this.required_fields, selected_fields);
|
||||||
|
if (missing_fields.length) {
|
||||||
|
this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
stop: function() {
|
||||||
|
$(this.$dialog).remove();
|
||||||
|
this._super();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -118,26 +118,26 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
|
||||||
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
|
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
|
||||||
Date.CultureInfo.formatPatterns.longTime));
|
Date.CultureInfo.formatPatterns.longTime));
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.datetime_to_str(tmp);
|
||||||
tmp = Date.parse(value);
|
tmp = Date.parse(value);
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.datetime_to_str(tmp);
|
||||||
throw value + " is not a valid datetime";
|
throw value + " is not a valid datetime";
|
||||||
case 'date':
|
case 'date':
|
||||||
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate);
|
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate);
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.date_to_str(tmp);
|
||||||
tmp = Date.parse(value);
|
tmp = Date.parse(value);
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.date_to_str(tmp);
|
||||||
throw value + " is not a valid date";
|
throw value + " is not a valid date";
|
||||||
case 'time':
|
case 'time':
|
||||||
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime);
|
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime);
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.time_to_str(tmp);
|
||||||
tmp = Date.parse(value);
|
tmp = Date.parse(value);
|
||||||
if (tmp !== null)
|
if (tmp !== null)
|
||||||
return tmp;
|
return openerp.web.time_to_str(tmp);
|
||||||
throw value + " is not a valid time";
|
throw value + " is not a valid time";
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -795,20 +795,18 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
|
||||||
* @extends openerp.web.search.DateField
|
* @extends openerp.web.search.DateField
|
||||||
*/
|
*/
|
||||||
openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{
|
openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{
|
||||||
/**
|
template: "SearchView.date",
|
||||||
* enables date picker on the HTML widgets
|
|
||||||
*/
|
|
||||||
start: function () {
|
start: function () {
|
||||||
this._super();
|
this._super();
|
||||||
this.$element.addClass('field_date').datepicker({
|
this.datewidget = new openerp.web.DateWidget(this);
|
||||||
dateFormat: 'yy-mm-dd'
|
this.datewidget.prependTo(this.$element);
|
||||||
});
|
this.datewidget.$element.find("input").attr("size", 15);
|
||||||
},
|
this.datewidget.$element.find("input").attr("autofocus",
|
||||||
stop: function () {
|
this.attrs.default_focus === '1' ? 'autofocus' : undefined);
|
||||||
this.$element.datepicker('destroy');
|
this.datewidget.set_value(this.defaults[this.attrs.name] || false);
|
||||||
},
|
},
|
||||||
get_value: function () {
|
get_value: function () {
|
||||||
return this.$element.val();
|
return this.datewidget.get_value() || null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/**
|
/**
|
||||||
|
@ -1123,7 +1121,7 @@ openerp.web.search.ExtendedSearchProposition.Char = openerp.web.OldWidget.extend
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.extend({
|
openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.extend({
|
||||||
template: 'SearchView.extended_search.proposition.datetime',
|
template: 'SearchView.extended_search.proposition.empty',
|
||||||
identifier_prefix: 'extended-search-proposition-datetime',
|
identifier_prefix: 'extended-search-proposition-datetime',
|
||||||
operators: [
|
operators: [
|
||||||
{value: "=", text: "is equal to"},
|
{value: "=", text: "is equal to"},
|
||||||
|
@ -1134,18 +1132,16 @@ openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.ex
|
||||||
{value: "<=", text: "less or equal than"}
|
{value: "<=", text: "less or equal than"}
|
||||||
],
|
],
|
||||||
get_value: function() {
|
get_value: function() {
|
||||||
return this.$element.val();
|
return this.datewidget.get_value();
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.$element.datetimepicker({
|
this.datewidget = new openerp.web.DateTimeWidget(this);
|
||||||
dateFormat: 'yy-mm-dd',
|
this.datewidget.prependTo(this.$element);
|
||||||
timeFormat: 'hh:mm:ss'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend({
|
openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend({
|
||||||
template: 'SearchView.extended_search.proposition.date',
|
template: 'SearchView.extended_search.proposition.empty',
|
||||||
identifier_prefix: 'extended-search-proposition-date',
|
identifier_prefix: 'extended-search-proposition-date',
|
||||||
operators: [
|
operators: [
|
||||||
{value: "=", text: "is equal to"},
|
{value: "=", text: "is equal to"},
|
||||||
|
@ -1156,14 +1152,12 @@ openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend
|
||||||
{value: "<=", text: "less or equal than"}
|
{value: "<=", text: "less or equal than"}
|
||||||
],
|
],
|
||||||
get_value: function() {
|
get_value: function() {
|
||||||
return this.$element.val();
|
return this.datewidget.get_value();
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.$element.datepicker({
|
this.datewidget = new openerp.web.DateWidget(this);
|
||||||
dateFormat: 'yy-mm-dd',
|
this.datewidget.prependTo(this.$element);
|
||||||
timeFormat: 'hh:mm:ss'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.extend({
|
openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.extend({
|
||||||
|
|
|
@ -1007,19 +1007,18 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||||
* the fields'context with the action's context.
|
* the fields'context with the action's context.
|
||||||
*/
|
*/
|
||||||
build_context: function() {
|
build_context: function() {
|
||||||
// I previously belevied contexts should be herrited, but now I doubt it
|
|
||||||
//var a_context = this.view.dataset.get_context() || {};
|
|
||||||
var f_context = this.field.context || null;
|
var f_context = this.field.context || null;
|
||||||
// maybe the default_get should only be used when we do a default_get?
|
// maybe the default_get should only be used when we do a default_get?
|
||||||
var v_context1 = this.node.attrs.default_get || {};
|
var v_contexts = _.compact([this.node.attrs.default_get || null,
|
||||||
var v_context2 = this.node.attrs.context || {};
|
this.node.attrs.context || null]);
|
||||||
var v_context = new openerp.web.CompoundContext(v_context1, v_context2);
|
var v_context = new openerp.web.CompoundContext();
|
||||||
if (v_context1.__ref || v_context2.__ref || true) { //TODO niv: remove || true
|
_.each(v_contexts, function(x) {v_context.add(x);});
|
||||||
|
if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
|
||||||
var fields_values = this._build_view_fields_values();
|
var fields_values = this._build_view_fields_values();
|
||||||
v_context.set_eval_context(fields_values);
|
v_context.set_eval_context(fields_values);
|
||||||
}
|
}
|
||||||
// if there is a context on the node, overrides the model's context
|
// if there is a context on the node, overrides the model's context
|
||||||
var ctx = f_context || v_context;
|
var ctx = v_contexts.length > 0 ? v_context : f_context;
|
||||||
return ctx;
|
return ctx;
|
||||||
},
|
},
|
||||||
build_domain: function() {
|
build_domain: function() {
|
||||||
|
@ -1121,16 +1120,13 @@ openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
openerp.web.DateTimeWidget = openerp.web.Widget.extend({
|
||||||
init: function(view, node) {
|
template: "web.datetimepicker",
|
||||||
this._super(view, node);
|
jqueryui_object: 'datetimepicker',
|
||||||
this.template = "FieldDate";
|
type_of_date: "datetime",
|
||||||
this.jqueryui_object = 'datetimepicker';
|
|
||||||
},
|
|
||||||
start: function() {
|
start: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this._super.apply(this, arguments);
|
this.$element.find('input').change(this.on_change);
|
||||||
this.$element.find('input').change(this.on_ui_change);
|
|
||||||
this.picker({
|
this.picker({
|
||||||
onSelect: this.on_picker_select,
|
onSelect: this.on_picker_select,
|
||||||
changeMonth: true,
|
changeMonth: true,
|
||||||
|
@ -1148,6 +1144,8 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||||
this.$element.find('button.oe_datepicker_close').click(function() {
|
this.$element.find('button.oe_datepicker_close').click(function() {
|
||||||
self.$element.find('.oe_datepicker').hide();
|
self.$element.find('.oe_datepicker').hide();
|
||||||
});
|
});
|
||||||
|
this.set_readonly(false);
|
||||||
|
this.value = false;
|
||||||
},
|
},
|
||||||
picker: function() {
|
picker: function() {
|
||||||
return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments);
|
return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments);
|
||||||
|
@ -1157,60 +1155,93 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||||
this.$element.find('input').val(date ? this.format_client(date) : '').change();
|
this.$element.find('input').val(date ? this.format_client(date) : '').change();
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
value = this.parse(value);
|
this.value = value;
|
||||||
this._super(value);
|
|
||||||
this.$element.find('input').val(value ? this.format_client(value) : '');
|
this.$element.find('input').val(value ? this.format_client(value) : '');
|
||||||
},
|
},
|
||||||
get_value: function() {
|
get_value: function() {
|
||||||
return this.format(this.value);
|
return this.value;
|
||||||
},
|
},
|
||||||
set_value_from_ui: function() {
|
set_value_from_ui: function() {
|
||||||
var value = this.$element.find('input').val() || false;
|
var value = this.$element.find('input').val() || false;
|
||||||
this.value = this.parse_client(value);
|
this.value = this.parse_client(value);
|
||||||
this._super();
|
|
||||||
},
|
},
|
||||||
update_dom: function() {
|
set_readonly: function(readonly) {
|
||||||
this._super.apply(this, arguments);
|
this.readonly = readonly;
|
||||||
this.$element.find('input').attr('disabled', this.readonly);
|
this.$element.find('input').attr('disabled', this.readonly);
|
||||||
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', this.readonly);
|
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
|
||||||
},
|
},
|
||||||
validate: function() {
|
is_valid: function(required) {
|
||||||
this.invalid = false;
|
|
||||||
var value = this.$element.find('input').val();
|
var value = this.$element.find('input').val();
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
this.invalid = this.required;
|
return !required;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
this.parse_client(value);
|
this.parse_client(value);
|
||||||
this.invalid = false;
|
return true;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
this.invalid = true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
focus: function() {
|
focus: function() {
|
||||||
this.$element.find('input').focus();
|
this.$element.find('input').focus();
|
||||||
},
|
},
|
||||||
parse: openerp.web.auto_str_to_date,
|
|
||||||
parse_client: function(v) {
|
parse_client: function(v) {
|
||||||
return openerp.web.parse_value(v, this.field);
|
return openerp.web.parse_value(v, {"widget": this.type_of_date});
|
||||||
},
|
|
||||||
format: function(val) {
|
|
||||||
return openerp.web.auto_date_to_str(val, this.field.type);
|
|
||||||
},
|
},
|
||||||
format_client: function(v) {
|
format_client: function(v) {
|
||||||
return openerp.web.format_value(v, this.field);
|
return openerp.web.format_value(v, {"widget": this.type_of_date});
|
||||||
|
},
|
||||||
|
on_change: function() {
|
||||||
|
if (this.is_valid()) {
|
||||||
|
this.set_value_from_ui();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
|
||||||
|
jqueryui_object: 'datepicker',
|
||||||
|
type_of_date: "date",
|
||||||
|
on_picker_select: function(text, instance) {
|
||||||
|
this._super(text, instance);
|
||||||
|
this.$element.find('.oe_datepicker').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||||
|
template: "EmptyComponent",
|
||||||
|
build_widget: function() {
|
||||||
|
return new openerp.web.DateTimeWidget(this);
|
||||||
|
},
|
||||||
|
start: function() {
|
||||||
|
var self = this;
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.datewidget = this.build_widget();
|
||||||
|
this.datewidget.on_change.add(this.on_ui_change);
|
||||||
|
this.datewidget.appendTo(this.$element);
|
||||||
|
},
|
||||||
|
set_value: function(value) {
|
||||||
|
this._super(value);
|
||||||
|
this.datewidget.set_value(value);
|
||||||
|
},
|
||||||
|
get_value: function() {
|
||||||
|
return this.datewidget.get_value();
|
||||||
|
},
|
||||||
|
update_dom: function() {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
this.datewidget.set_readonly(this.readonly);
|
||||||
|
},
|
||||||
|
validate: function() {
|
||||||
|
this.invalid = !this.datewidget.is_valid(this.required);
|
||||||
|
},
|
||||||
|
focus: function() {
|
||||||
|
this.datewidget.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
||||||
init: function(view, node) {
|
build_widget: function() {
|
||||||
this._super(view, node);
|
return new openerp.web.DateWidget(this);
|
||||||
this.jqueryui_object = 'datepicker';
|
|
||||||
},
|
|
||||||
on_picker_select: function(text, instance) {
|
|
||||||
this._super(text, instance);
|
|
||||||
this.$element.find('.oe_datepicker').hide();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1436,7 +1467,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||||
this.$input = this.$element.find("input");
|
this.$input = this.$element.find("input");
|
||||||
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
|
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
|
||||||
this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
|
this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
|
||||||
|
|
||||||
// context menu
|
// context menu
|
||||||
var init_context_menu_def = $.Deferred().then(function(e) {
|
var init_context_menu_def = $.Deferred().then(function(e) {
|
||||||
var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
|
var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
|
||||||
|
@ -1444,7 +1475,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||||
[[self.field.relation, false]], false, rdataset.get_context()], false, 0)
|
[[self.field.relation, false]], false, rdataset.get_context()], false, 0)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
self.related_entries = result;
|
self.related_entries = result;
|
||||||
|
|
||||||
var $cmenu = $("#" + self.cm_id);
|
var $cmenu = $("#" + self.cm_id);
|
||||||
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
|
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
|
||||||
var bindings = {};
|
var bindings = {};
|
||||||
|
|
|
@ -415,8 +415,8 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||||
*/
|
*/
|
||||||
do_search: function (domains, contexts, groupbys) {
|
do_search: function (domains, contexts, groupbys) {
|
||||||
return this.rpc('/web/session/eval_domain_and_context', {
|
return this.rpc('/web/session/eval_domain_and_context', {
|
||||||
domains: [this.dataset.get_domain()].concat(domains),
|
domains: _([this.dataset.get_domain()].concat(domains)).compact(),
|
||||||
contexts: [this.dataset.get_context()].concat(contexts),
|
contexts: _([this.dataset.get_context()].concat(contexts)).compact(),
|
||||||
group_by_seq: groupbys
|
group_by_seq: groupbys
|
||||||
}, $.proxy(this, 'do_actual_search'));
|
}, $.proxy(this, 'do_actual_search'));
|
||||||
},
|
},
|
||||||
|
@ -541,6 +541,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||||
if (_.isEmpty(records)) {
|
if (_.isEmpty(records)) {
|
||||||
records = this.groups.get_records();
|
records = this.groups.get_records();
|
||||||
}
|
}
|
||||||
|
records = _(records).compact();
|
||||||
|
|
||||||
var count = 0, sums = {};
|
var count = 0, sums = {};
|
||||||
_(columns).each(function (column) {
|
_(columns).each(function (column) {
|
||||||
|
@ -1235,6 +1236,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
|
||||||
},
|
},
|
||||||
get_records: function () {
|
get_records: function () {
|
||||||
if (_(this.children).isEmpty()) {
|
if (_(this.children).isEmpty()) {
|
||||||
|
if (!this.datagroup.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
count: this.datagroup.length,
|
count: this.datagroup.length,
|
||||||
values: this.datagroup.aggregates
|
values: this.datagroup.aggregates
|
||||||
|
|
|
@ -862,6 +862,8 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
|
||||||
console.log('Todo');
|
console.log('Todo');
|
||||||
},
|
},
|
||||||
on_sidebar_import: function() {
|
on_sidebar_import: function() {
|
||||||
|
var import_view = new db.web.DataImport(this, this.dataset);
|
||||||
|
import_view.start();
|
||||||
},
|
},
|
||||||
on_sidebar_export: function() {
|
on_sidebar_export: function() {
|
||||||
var export_view = new db.web.DataExport(this, this.dataset);
|
var export_view = new db.web.DataExport(this, this.dataset);
|
||||||
|
|
|
@ -800,13 +800,15 @@
|
||||||
></textarea>
|
></textarea>
|
||||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="FieldDate">
|
<t t-name="web.datetimepicker">
|
||||||
<t t-call="FieldChar"/>
|
<div class="oe_datepicker_root">
|
||||||
<img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
|
<input type="text" size="1" style="width: 100%"/>
|
||||||
title="Select date" width="16" height="16" border="0"/>
|
<img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
|
||||||
<div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
|
title="Select date" width="16" height="16" border="0"/>
|
||||||
<div class="oe_datepicker_container"/>
|
<div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
|
||||||
<button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
|
<div class="oe_datepicker_container"/>
|
||||||
|
<button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="FieldSelection">
|
<t t-name="FieldSelection">
|
||||||
|
@ -1051,6 +1053,18 @@
|
||||||
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
|
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
<t t-name="SearchView.date">
|
||||||
|
<label t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
|
||||||
|
t-att-title="attrs.help"
|
||||||
|
t-att-for="element_id">
|
||||||
|
<t t-esc="attrs.string || attrs.name"/>
|
||||||
|
<span t-if="attrs.help">?</span>
|
||||||
|
</label>
|
||||||
|
<div style="white-space: nowrap;">
|
||||||
|
<span t-att-id="element_id"></span>
|
||||||
|
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
<t t-name="SearchView.field.selection">
|
<t t-name="SearchView.field.selection">
|
||||||
<label t-att-title="attrs.help"
|
<label t-att-title="attrs.help"
|
||||||
t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
|
t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
|
||||||
|
@ -1142,11 +1156,8 @@
|
||||||
<t t-name="SearchView.extended_search.proposition.char">
|
<t t-name="SearchView.extended_search.proposition.char">
|
||||||
<input t-att-id="element_id" class="field_char"/>
|
<input t-att-id="element_id" class="field_char"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="SearchView.extended_search.proposition.datetime">
|
<t t-name="SearchView.extended_search.proposition.empty">
|
||||||
<input t-att-id="element_id" class="field_datetime"/>
|
<span t-att-id="element_id"></span>
|
||||||
</t>
|
|
||||||
<t t-name="SearchView.extended_search.proposition.date">
|
|
||||||
<input t-att-id="element_id" class="field_date"/>
|
|
||||||
</t>
|
</t>
|
||||||
<t t-name="SearchView.extended_search.proposition.integer">
|
<t t-name="SearchView.extended_search.proposition.integer">
|
||||||
<input type="number" t-att-id="element_id" class="field_integer" step="1"/>
|
<input type="number" t-att-id="element_id" class="field_integer" step="1"/>
|
||||||
|
@ -1372,6 +1383,79 @@
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
|
<t t-name="ImportView">
|
||||||
|
<a id="importview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Import</a>
|
||||||
|
</t>
|
||||||
|
<t t-name="ImportDataView">
|
||||||
|
<form name="import_data" id="import_data" action="" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="session_id" t-att-value="session.session_id"/>
|
||||||
|
<h2 class="separator horizontal">1. Import a .CSV file</h2>
|
||||||
|
<p>Select a .CSV file to import. If you need a sample of file to import,
|
||||||
|
you should use the export tool with the "Import Compatible" option.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="csvfile">CSV File:</label>
|
||||||
|
<input type="file" id="csvfile" size="50" name="csvfile"/>
|
||||||
|
</p>
|
||||||
|
<h2 class="separator horizontal">2. Check your file format</h2>
|
||||||
|
<div id="result"></div>
|
||||||
|
<fieldset>
|
||||||
|
<legend style="cursor:pointer;">Import Options</legend>
|
||||||
|
<table style="display:none">
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<label for="file_has_headers">Does your file have titles?</label>
|
||||||
|
<input type="checkbox" checked="checked"
|
||||||
|
id="file_has_headers"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="csv_separator">Separator:</label></td>
|
||||||
|
<td><input type="text" name="csvsep" id="csv_separator" value=","/></td>
|
||||||
|
<td><label for="csv_delimiter">Delimiter:</label></td>
|
||||||
|
<td><input type="text" name="csvdel" id="csv_delimiter" value='"'/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="csv_encoding">Encoding:</label></td>
|
||||||
|
<td>
|
||||||
|
<select name="csvcode" id="csv_encoding">
|
||||||
|
<option value="utf-8">UTF-8</option>
|
||||||
|
<option value="latin1">Latin 1</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><label for="csv_skip" title="For use if CSV files have titles on multiple lines, skips more than a single line during import">
|
||||||
|
Lines to skip<sup>?</sup>:</label></td>
|
||||||
|
<td><input type="number" id="csv_skip" value="0" min="0"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</t>
|
||||||
|
<table t-name="ImportView.result"
|
||||||
|
class="oe_import_grid" width="100%" style="margin: 5px 0;">
|
||||||
|
<tr t-if="headers" class="oe_import_grid-header">
|
||||||
|
<td t-foreach="headers" t-as="header" class="oe_import_grid-cell">
|
||||||
|
<t t-esc="header"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td t-foreach="records[0]" t-as="column">
|
||||||
|
<input class="sel_fields"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr t-foreach="records" t-as="record" class="oe_import_grid-row">
|
||||||
|
<td t-foreach="record" t-as="cell" class="oe_import_grid-cell">
|
||||||
|
<t t-esc="cell"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<t t-name="ImportView.error">
|
||||||
|
<p style="white-space:pre-line;">The import failed due to:<t t-esc="error.message"/></p>
|
||||||
|
<t t-if="error.preview">
|
||||||
|
<p>Here is a preview of the file we could not import:</p>
|
||||||
|
<pre><t t-esc="error.preview"/></pre>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
<t t-name="About-Page">
|
<t t-name="About-Page">
|
||||||
<div>
|
<div>
|
||||||
<h1>OpenERP Web</h1>
|
<h1>OpenERP Web</h1>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
|
|
||||||
import werkzeug.serving
|
import werkzeug.serving
|
||||||
|
import werkzeug.contrib.fixers
|
||||||
|
|
||||||
path_root = os.path.dirname(os.path.abspath(__file__))
|
path_root = os.path.dirname(os.path.abspath(__file__))
|
||||||
path_addons = os.path.join(path_root, 'addons')
|
path_addons = os.path.join(path_root, 'addons')
|
||||||
|
@ -14,8 +15,6 @@ if path_addons not in sys.path:
|
||||||
sys.path.insert(0, path_addons)
|
sys.path.insert(0, path_addons)
|
||||||
|
|
||||||
optparser = optparse.OptionParser()
|
optparser = optparse.OptionParser()
|
||||||
optparser.add_option("-p", "--port", dest="socket_port", default=8002,
|
|
||||||
help="listening port", type="int", metavar="NUMBER")
|
|
||||||
optparser.add_option("-s", "--session-path", dest="session_storage",
|
optparser.add_option("-s", "--session-path", dest="session_storage",
|
||||||
default=os.path.join(tempfile.gettempdir(), "oe-sessions"),
|
default=os.path.join(tempfile.gettempdir(), "oe-sessions"),
|
||||||
help="directory used for session storage", metavar="DIR")
|
help="directory used for session storage", metavar="DIR")
|
||||||
|
@ -27,25 +26,37 @@ optparser.add_option("--db-filter", dest="dbfilter", default='.*',
|
||||||
help="Filter listed database", metavar="REGEXP")
|
help="Filter listed database", metavar="REGEXP")
|
||||||
optparser.add_option('--addons-path', dest='addons_path', default=path_addons,
|
optparser.add_option('--addons-path', dest='addons_path', default=path_addons,
|
||||||
help="Path do addons directory", metavar="PATH")
|
help="Path do addons directory", metavar="PATH")
|
||||||
optparser.add_option('--no-serve-static', dest='serve_static',
|
|
||||||
default=True, action='store_false',
|
server_options = optparse.OptionGroup(optparser, "Server configuration")
|
||||||
help="Do not serve static files via this server")
|
server_options.add_option("-p", "--port", dest="socket_port", default=8002,
|
||||||
optparser.add_option('--reloader', dest='reloader',
|
help="listening port", type="int", metavar="NUMBER")
|
||||||
default=False, action='store_true',
|
server_options.add_option('--reloader', dest='reloader',
|
||||||
help="Reload application when python files change")
|
default=False, action='store_true',
|
||||||
optparser.add_option("--log-level", dest="log_level",
|
help="Reload application when python files change")
|
||||||
default='debug', help="Log level", metavar="LOG_LEVEL")
|
server_options.add_option('--no-serve-static', dest='serve_static',
|
||||||
optparser.add_option("--log-config", dest="log_config",
|
default=True, action='store_false',
|
||||||
default='', help="Log config file", metavar="LOG_CONFIG")
|
help="Do not serve static files via this server")
|
||||||
optparser.add_option('--multi-threaded', dest='threaded',
|
server_options.add_option('--multi-threaded', dest='threaded',
|
||||||
default=False, action='store_true',
|
default=False, action='store_true',
|
||||||
help="Use multiple threads to handle requests")
|
help="Spawn one thread per HTTP request")
|
||||||
|
server_options.add_option('--proxy-mode', dest='proxy_mode',
|
||||||
|
default=False, action='store_true',
|
||||||
|
help="Enable correct behavior when behind a reverse proxy")
|
||||||
|
optparser.add_option_group(server_options)
|
||||||
|
|
||||||
|
logging_opts = optparse.OptionGroup(optparser, "Logging")
|
||||||
|
logging_opts.add_option("--log-level", dest="log_level", type="choice",
|
||||||
|
default='debug', help="Global logging level", metavar="LOG_LEVEL",
|
||||||
|
choices=['debug', 'info', 'warning', 'error', 'critical'])
|
||||||
|
logging_opts.add_option("--log-config", dest="log_config",
|
||||||
|
help="Logging configuration file", metavar="FILE")
|
||||||
|
optparser.add_option_group(logging_opts)
|
||||||
|
|
||||||
import web.common.dispatch
|
import web.common.dispatch
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
(options, args) = optparser.parse_args(sys.argv[1:])
|
(options, args) = optparser.parse_args(sys.argv[1:])
|
||||||
options.backend = 'rpc'
|
options.backend = 'xmlrpc'
|
||||||
|
|
||||||
os.environ["TZ"] = "UTC"
|
os.environ["TZ"] = "UTC"
|
||||||
|
|
||||||
|
@ -56,6 +67,9 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
app = web.common.dispatch.Root(options)
|
app = web.common.dispatch.Root(options)
|
||||||
|
|
||||||
|
if options.proxy_mode:
|
||||||
|
app = werkzeug.contrib.fixers.ProxyFix(app)
|
||||||
|
|
||||||
werkzeug.serving.run_simple(
|
werkzeug.serving.run_simple(
|
||||||
'0.0.0.0', options.socket_port, app,
|
'0.0.0.0', options.socket_port, app,
|
||||||
use_reloader=options.reloader, threaded=options.threaded)
|
use_reloader=options.reloader, threaded=options.threaded)
|
||||||
|
|
Loading…
Reference in New Issue