[MERGE] Latest trunk.
bzr revid: vta@openerp.com-20121015082458-rudm5r0718ky7829
This commit is contained in:
commit
68cf1f2d6a
34
Makefile
34
Makefile
|
@ -1,34 +0,0 @@
|
|||
.PHONY: all doc release clean
|
||||
|
||||
HOST = 127.0.0.1
|
||||
PORT = 8080
|
||||
|
||||
all: run
|
||||
|
||||
run:
|
||||
python openerp-web.py -a ${HOST} -p ${PORT}
|
||||
|
||||
release:
|
||||
python setup.py sdist
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
|
||||
clean:
|
||||
@find . -name '*.pyc' -exec rm -f {} +
|
||||
@find . -name '*.pyo' -exec rm -f {} +
|
||||
@find . -name '*.swp' -exec rm -f {} +
|
||||
@find . -name '*~' -exec rm -f {} +
|
||||
@rm -rf build
|
||||
@rm -rf dist
|
||||
@rm -rf *.egg-info
|
||||
|
||||
doc:
|
||||
make -C doc html
|
||||
|
||||
cloc:
|
||||
cloc addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/*.js addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml
|
||||
|
||||
blamestat:
|
||||
echo addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml | xargs -t -n 1 bzr blame -v --long --all | awk '{print $2}' | sort | uniq -c | sort -n
|
||||
|
|
@ -8,9 +8,4 @@ The OpenERP Web Client supports the following web browsers:
|
|||
* Firefox 13+
|
||||
* Any browser using the latest version of Chrome Frame
|
||||
|
||||
To build the documentation use:
|
||||
|
||||
$ make doc
|
||||
|
||||
then look at doc/build/html/index.html
|
||||
|
||||
|
|
|
@ -1,31 +1,4 @@
|
|||
import logging
|
||||
|
||||
from . import common
|
||||
from . import controllers
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Options(object):
|
||||
pass
|
||||
|
||||
def wsgi_postload():
|
||||
import openerp
|
||||
import os
|
||||
import tempfile
|
||||
import getpass
|
||||
_logger.info("embedded mode")
|
||||
o = Options()
|
||||
o.dbfilter = openerp.tools.config['dbfilter']
|
||||
o.server_wide_modules = openerp.conf.server_wide_modules or ['web']
|
||||
try:
|
||||
username = getpass.getuser()
|
||||
except Exception:
|
||||
username = "unknown"
|
||||
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
||||
o.addons_path = openerp.modules.module.ad_paths
|
||||
o.serve_static = True
|
||||
o.backend = 'local'
|
||||
|
||||
app = common.http.Root(o)
|
||||
openerp.wsgi.register_wsgi_handler(app)
|
||||
import http
|
||||
import controllers
|
||||
|
||||
wsgi_postload = http.wsgi_postload
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
from . import http
|
||||
from . import nonliterals
|
||||
from . import release
|
||||
from . import session
|
||||
from . import xml2json
|
|
@ -1,32 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) Stephane Wirtel
|
||||
# Copyright (C) 2011 Nicolas Vanhoren
|
||||
# Copyright (C) 2011 OpenERP s.a. (<http://openerp.com>).
|
||||
# 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from .main import *
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) Stephane Wirtel
|
||||
# Copyright (C) 2011 Nicolas Vanhoren
|
||||
# Copyright (C) 2011 OpenERP s.a. (<http://openerp.com>).
|
||||
# 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
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,311 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) Stephane Wirtel
|
||||
# Copyright (C) 2011 Nicolas Vanhoren
|
||||
# Copyright (C) 2011 OpenERP s.a. (<http://openerp.com>).
|
||||
# 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
OpenERP Client Library
|
||||
|
||||
Home page: http://pypi.python.org/pypi/openerp-client-lib
|
||||
Code repository: https://code.launchpad.net/~niv-openerp/openerp-client-lib/trunk
|
||||
"""
|
||||
|
||||
import xmlrpclib
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def _getChildLogger(logger, subname):
|
||||
return logging.getLogger(logger.name + "." + subname)
|
||||
|
||||
class Connector(object):
|
||||
"""
|
||||
The base abstract class representing a connection to an OpenERP Server.
|
||||
"""
|
||||
|
||||
__logger = _getChildLogger(_logger, 'connector')
|
||||
|
||||
def get_service(self, service_name):
|
||||
"""
|
||||
Returns a Service instance to allow easy manipulation of one of the services offered by the remote server.
|
||||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return Service(self, service_name)
|
||||
|
||||
class XmlRPCConnector(Connector):
|
||||
"""
|
||||
A type of connector that uses the XMLRPC protocol.
|
||||
"""
|
||||
PROTOCOL = 'xmlrpc'
|
||||
|
||||
__logger = _getChildLogger(_logger, 'connector.xmlrpc')
|
||||
|
||||
def __init__(self, hostname, port=8069):
|
||||
"""
|
||||
Initialize by specifying the hostname and the port.
|
||||
:param hostname: The hostname of the computer holding the instance of OpenERP.
|
||||
:param port: The port used by the OpenERP instance for XMLRPC (default to 8069).
|
||||
"""
|
||||
self.url = 'http://%s:%d/xmlrpc' % (hostname, port)
|
||||
|
||||
def send(self, service_name, method, *args):
|
||||
url = '%s/%s' % (self.url, service_name)
|
||||
service = xmlrpclib.ServerProxy(url)
|
||||
return getattr(service, method)(*args)
|
||||
|
||||
class XmlRPCSConnector(XmlRPCConnector):
|
||||
"""
|
||||
A type of connector that uses the secured XMLRPC protocol.
|
||||
"""
|
||||
PROTOCOL = 'xmlrpcs'
|
||||
|
||||
__logger = _getChildLogger(_logger, 'connector.xmlrpcs')
|
||||
|
||||
def __init__(self, hostname, port=8069):
|
||||
super(XmlRPCSConnector, self).__init__(hostname, port)
|
||||
self.url = 'https://%s:%d/xmlrpc' % (hostname, port)
|
||||
|
||||
class Service(object):
|
||||
"""
|
||||
A class to execute RPC calls on a specific service of the remote server.
|
||||
"""
|
||||
def __init__(self, connector, service_name):
|
||||
"""
|
||||
:param connector: A valid Connector instance.
|
||||
:param service_name: The name of the service on the remote server.
|
||||
"""
|
||||
self.connector = connector
|
||||
self.service_name = service_name
|
||||
self.__logger = _getChildLogger(_getChildLogger(_logger, 'service'),service_name or "")
|
||||
|
||||
def __getattr__(self, method):
|
||||
"""
|
||||
:param method: The name of the method to execute on the service.
|
||||
"""
|
||||
self.__logger.debug('method: %r', method)
|
||||
def proxy(*args):
|
||||
"""
|
||||
:param args: A list of values for the method
|
||||
"""
|
||||
self.__logger.debug('args: %r', args)
|
||||
result = self.connector.send(self.service_name, method, *args)
|
||||
self.__logger.debug('result: %r', result)
|
||||
return result
|
||||
return proxy
|
||||
|
||||
class Connection(object):
|
||||
"""
|
||||
A class to represent a connection with authentication to an OpenERP Server.
|
||||
It also provides utility methods to interact with the server more easily.
|
||||
"""
|
||||
__logger = _getChildLogger(_logger, 'connection')
|
||||
|
||||
def __init__(self, connector,
|
||||
database=None,
|
||||
login=None,
|
||||
password=None,
|
||||
user_id=None):
|
||||
"""
|
||||
Initialize with login information. The login information is facultative to allow specifying
|
||||
it after the initialization of this object.
|
||||
|
||||
:param connector: A valid Connector instance to send messages to the remote server.
|
||||
:param database: The name of the database to work on.
|
||||
:param login: The login of the user.
|
||||
:param password: The password of the user.
|
||||
:param user_id: The user id is a number identifying the user. This is only useful if you
|
||||
already know it, in most cases you don't need to specify it.
|
||||
"""
|
||||
self.connector = connector
|
||||
|
||||
self.set_login_info(database, login, password, user_id)
|
||||
self.user_context = None
|
||||
|
||||
def set_login_info(self, database, login, password, user_id=None):
|
||||
"""
|
||||
Set login information after the initialisation of this object.
|
||||
|
||||
:param connector: A valid Connector instance to send messages to the remote server.
|
||||
:param database: The name of the database to work on.
|
||||
:param login: The login of the user.
|
||||
:param password: The password of the user.
|
||||
:param user_id: The user id is a number identifying the user. This is only useful if you
|
||||
already know it, in most cases you don't need to specify it.
|
||||
"""
|
||||
self.database, self.login, self.password = database, login, password
|
||||
|
||||
self.user_id = user_id
|
||||
|
||||
def check_login(self, force=True):
|
||||
"""
|
||||
Checks that the login information is valid. Throws an AuthenticationError if the
|
||||
authentication fails.
|
||||
|
||||
:param force: Force to re-check even if this Connection was already validated previously.
|
||||
Default to True.
|
||||
"""
|
||||
if self.user_id and not force:
|
||||
return
|
||||
|
||||
if not self.database or not self.login or self.password is None:
|
||||
raise AuthenticationError("Credentials not provided")
|
||||
|
||||
# TODO use authenticate instead of login
|
||||
self.user_id = self.get_service("common").login(self.database, self.login, self.password)
|
||||
if not self.user_id:
|
||||
raise AuthenticationError("Authentication failure")
|
||||
self.__logger.debug("Authenticated with user id %s", self.user_id)
|
||||
|
||||
def get_user_context(self):
|
||||
"""
|
||||
Query the default context of the user.
|
||||
"""
|
||||
if not self.user_context:
|
||||
self.user_context = self.get_model('res.users').context_get()
|
||||
return self.user_context
|
||||
|
||||
def get_model(self, model_name):
|
||||
"""
|
||||
Returns a Model instance to allow easy remote manipulation of an OpenERP model.
|
||||
|
||||
:param model_name: The name of the model.
|
||||
"""
|
||||
return Model(self, model_name)
|
||||
|
||||
def get_service(self, service_name):
|
||||
"""
|
||||
Returns a Service instance to allow easy manipulation of one of the services offered by the remote server.
|
||||
Please note this Connection instance does not need to have valid authentication information since authentication
|
||||
is only necessary for the "object" service that handles models.
|
||||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return self.connector.get_service(service_name)
|
||||
|
||||
class AuthenticationError(Exception):
|
||||
"""
|
||||
An error thrown when an authentication to an OpenERP server failed.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Model(object):
|
||||
"""
|
||||
Useful class to dialog with one of the models provided by an OpenERP server.
|
||||
An instance of this class depends on a Connection instance with valid authentication information.
|
||||
"""
|
||||
|
||||
def __init__(self, connection, model_name):
|
||||
"""
|
||||
:param connection: A valid Connection instance with correct authentication information.
|
||||
:param model_name: The name of the model.
|
||||
"""
|
||||
self.connection = connection
|
||||
self.model_name = model_name
|
||||
self.__logger = _getChildLogger(_getChildLogger(_logger, 'object'), model_name or "")
|
||||
|
||||
def __getattr__(self, method):
|
||||
"""
|
||||
Provides proxy methods that will forward calls to the model on the remote OpenERP server.
|
||||
|
||||
:param method: The method for the linked model (search, read, write, unlink, create, ...)
|
||||
"""
|
||||
def proxy(*args, **kw):
|
||||
"""
|
||||
:param args: A list of values for the method
|
||||
"""
|
||||
self.connection.check_login(False)
|
||||
self.__logger.debug(args)
|
||||
result = self.connection.get_service('object').execute_kw(
|
||||
self.connection.database,
|
||||
self.connection.user_id,
|
||||
self.connection.password,
|
||||
self.model_name,
|
||||
method,
|
||||
args, kw)
|
||||
if method == "read":
|
||||
if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
|
||||
index = {}
|
||||
for r in result:
|
||||
index[r['id']] = r
|
||||
result = [index[x] for x in args[0] if x in index]
|
||||
self.__logger.debug('result: %r', result)
|
||||
return result
|
||||
return proxy
|
||||
|
||||
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, context=None):
|
||||
"""
|
||||
A shortcut method to combine a search() and a read().
|
||||
|
||||
:param domain: The domain for the search.
|
||||
:param fields: The fields to extract (can be None or [] to extract all fields).
|
||||
:param offset: The offset for the rows to read.
|
||||
:param limit: The maximum number of rows to read.
|
||||
:param order: The order to class the rows.
|
||||
:param context: The context.
|
||||
:return: A list of dictionaries containing all the specified fields.
|
||||
"""
|
||||
record_ids = self.search(domain or [], offset, limit or False, order or False, context or {})
|
||||
if not record_ids: return []
|
||||
records = self.read(record_ids, fields or [], context or {})
|
||||
return records
|
||||
|
||||
def get_connector(hostname=None, protocol="xmlrpc", port="auto"):
|
||||
"""
|
||||
A shortcut method to easily create a connector to a remote server using XMLRPC.
|
||||
|
||||
:param hostname: The hostname to the remote server.
|
||||
:param protocol: The name of the protocol, must be "xmlrpc" or "xmlrpcs".
|
||||
:param port: The number of the port. Defaults to auto.
|
||||
"""
|
||||
if port == 'auto':
|
||||
port = 8069
|
||||
if protocol == "xmlrpc":
|
||||
return XmlRPCConnector(hostname, port)
|
||||
elif protocol == "xmlrpcs":
|
||||
return XmlRPCSConnector(hostname, port)
|
||||
else:
|
||||
raise ValueError("You must choose xmlrpc or xmlrpcs")
|
||||
|
||||
def get_connection(hostname=None, protocol="xmlrpc", port='auto', database=None,
|
||||
login=None, password=None, user_id=None):
|
||||
"""
|
||||
A shortcut method to easily create a connection to a remote OpenERP server.
|
||||
|
||||
:param hostname: The hostname to the remote server.
|
||||
:param protocol: The name of the protocol, must be "xmlrpc" or "xmlrpcs".
|
||||
:param port: The number of the port. Defaults to auto.
|
||||
:param connector: A valid Connector instance to send messages to the remote server.
|
||||
:param database: The name of the database to work on.
|
||||
:param login: The login of the user.
|
||||
:param password: The password of the user.
|
||||
:param user_id: The user id is a number identifying the user. This is only useful if you
|
||||
already know it, in most cases you don't need to specify it.
|
||||
"""
|
||||
return Connection(get_connector(hostname, protocol, port), database, login, password, user_id)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
name = 'openerp-web'
|
||||
version = '7.0alpha'
|
||||
description = "OpenERP Web"
|
||||
long_description = "OpenERP Web is the web interface of OpenERP, an Open Source Business Application Suite"
|
||||
author = "OpenERP SA"
|
||||
author_email = "info@openerp.com"
|
||||
support_email = 'support@openerp.com'
|
||||
url = "http://www.openerp.com/"
|
||||
download_url = ''
|
||||
license = "AGPL"
|
|
@ -1,26 +0,0 @@
|
|||
# xml2json-direct
|
||||
# Simple and straightforward XML-to-JSON converter in Python
|
||||
# New BSD Licensed
|
||||
#
|
||||
# URL: http://code.google.com/p/xml2json-direct/
|
||||
|
||||
def from_elementtree(el, preserve_whitespaces=False):
|
||||
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 (preserve_whitespaces or el.text.strip() != ''):
|
||||
kids.append(el.text)
|
||||
for kid in el:
|
||||
kids.append(from_elementtree(kid, preserve_whitespaces))
|
||||
if kid.tail and (preserve_whitespaces or kid.tail.strip() != ''):
|
||||
kids.append(kid.tail)
|
||||
res["children"] = kids
|
||||
return res
|
|
@ -27,8 +27,11 @@ try:
|
|||
except ImportError:
|
||||
xlwt = None
|
||||
|
||||
from .. import common
|
||||
openerpweb = common.http
|
||||
import openerp
|
||||
|
||||
from .. import http
|
||||
from .. import nonliterals
|
||||
openerpweb = http
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web helpers
|
||||
|
@ -135,7 +138,7 @@ def db_list(req):
|
|||
dbs = proxy.list()
|
||||
h = req.httprequest.environ['HTTP_HOST'].split(':')[0]
|
||||
d = h.split('.')[0]
|
||||
r = req.config.dbfilter.replace('%h', h).replace('%d', d)
|
||||
r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
|
||||
dbs = [i for i in dbs if re.match(r, i)]
|
||||
return dbs
|
||||
|
||||
|
@ -227,11 +230,12 @@ def module_installed_bypass_session(dbname):
|
|||
return sorted_modules
|
||||
|
||||
def module_boot(req):
|
||||
return [m for m in req.config.server_wide_modules if m in openerpweb.addons_manifest]
|
||||
server_wide_modules = openerp.conf.server_wide_modules or ['web']
|
||||
return [m for m in server_wide_modules if m in openerpweb.addons_manifest]
|
||||
# TODO the following will be enabled once we separate the module code and translation loading
|
||||
serverside = []
|
||||
dbside = []
|
||||
for i in req.config.server_wide_modules:
|
||||
for i in server_wide_modules:
|
||||
if i in openerpweb.addons_manifest:
|
||||
serverside.append(i)
|
||||
# if only one db load every module at boot
|
||||
|
@ -502,12 +506,12 @@ def fix_view_modes(action):
|
|||
|
||||
def parse_domain(domain, session):
|
||||
""" Parses an arbitrary string containing a domain, transforms it
|
||||
to either a literal domain or a :class:`common.nonliterals.Domain`
|
||||
to either a literal domain or a :class:`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
|
||||
:param session: Current OpenERP session
|
||||
:type session: openerpweb.openerpweb.OpenERPSession
|
||||
:type session: openerpweb.OpenERPSession
|
||||
"""
|
||||
if not isinstance(domain, basestring):
|
||||
return domain
|
||||
|
@ -515,24 +519,23 @@ def parse_domain(domain, session):
|
|||
return ast.literal_eval(domain)
|
||||
except ValueError:
|
||||
# not a literal
|
||||
return common.nonliterals.Domain(session, domain)
|
||||
return nonliterals.Domain(session, domain)
|
||||
|
||||
def parse_context(context, session):
|
||||
""" Parses an arbitrary string containing a context, transforms it
|
||||
to either a literal context or a :class:`common.nonliterals.Context`
|
||||
to either a literal context or a :class:`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
|
||||
:param session: Current OpenERP session
|
||||
:type session: openerpweb.openerpweb.OpenERPSession
|
||||
:type session: openerpweb.OpenERPSession
|
||||
"""
|
||||
if not isinstance(context, basestring):
|
||||
return context
|
||||
try:
|
||||
return ast.literal_eval(context)
|
||||
except ValueError:
|
||||
return common.nonliterals.Context(session, context)
|
||||
|
||||
return nonliterals.Context(session, context)
|
||||
|
||||
def _local_web_translations(trans_file):
|
||||
messages = []
|
||||
|
@ -546,6 +549,31 @@ def _local_web_translations(trans_file):
|
|||
messages.append({'id': x.id, 'string': x.string})
|
||||
return messages
|
||||
|
||||
def from_elementtree(el, preserve_whitespaces=False):
|
||||
""" xml2json-direct
|
||||
Simple and straightforward XML-to-JSON converter in Python
|
||||
New BSD Licensed
|
||||
http://code.google.com/p/xml2json-direct/
|
||||
"""
|
||||
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 (preserve_whitespaces or el.text.strip() != ''):
|
||||
kids.append(el.text)
|
||||
for kid in el:
|
||||
kids.append(from_elementtree(kid, preserve_whitespaces))
|
||||
if kid.tail and (preserve_whitespaces or kid.tail.strip() != ''):
|
||||
kids.append(kid.tail)
|
||||
res["children"] = kids
|
||||
return res
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web web Controllers
|
||||
|
@ -612,7 +640,6 @@ class Home(openerpweb.Controller):
|
|||
def login(self, req, db, login, key):
|
||||
return login_and_redirect(req, db, login, key)
|
||||
|
||||
|
||||
class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/web/webclient"
|
||||
|
||||
|
@ -746,7 +773,7 @@ class WebClient(openerpweb.Controller):
|
|||
@openerpweb.jsonrequest
|
||||
def version_info(self, req):
|
||||
return {
|
||||
"version": common.release.version
|
||||
"version": openerp.release.version
|
||||
}
|
||||
|
||||
class Proxy(openerpweb.Controller):
|
||||
|
@ -862,12 +889,10 @@ class Session(openerpweb.Controller):
|
|||
@openerpweb.jsonrequest
|
||||
def authenticate(self, req, db, login, password, base_location=None):
|
||||
wsgienv = req.httprequest.environ
|
||||
release = common.release
|
||||
env = dict(
|
||||
base_location=base_location,
|
||||
HTTP_HOST=wsgienv['HTTP_HOST'],
|
||||
REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
|
||||
user_agent="%s / %s" % (release.name, release.version),
|
||||
)
|
||||
req.session.authenticate(db, login, password, env)
|
||||
|
||||
|
@ -942,8 +967,8 @@ class Session(openerpweb.Controller):
|
|||
no group by should be performed)
|
||||
"""
|
||||
context, domain = eval_context_and_domain(req.session,
|
||||
common.nonliterals.CompoundContext(*(contexts or [])),
|
||||
common.nonliterals.CompoundDomain(*(domains or [])))
|
||||
nonliterals.CompoundContext(*(contexts or [])),
|
||||
nonliterals.CompoundDomain(*(domains or [])))
|
||||
|
||||
group_by_sequence = []
|
||||
for candidate in (group_by_seq or []):
|
||||
|
@ -1165,14 +1190,14 @@ class DataSet(openerpweb.Controller):
|
|||
|
||||
def _call_kw(self, req, model, method, args, kwargs):
|
||||
for i in xrange(len(args)):
|
||||
if isinstance(args[i], common.nonliterals.BaseContext):
|
||||
if isinstance(args[i], nonliterals.BaseContext):
|
||||
args[i] = req.session.eval_context(args[i])
|
||||
elif isinstance(args[i], common.nonliterals.BaseDomain):
|
||||
elif isinstance(args[i], nonliterals.BaseDomain):
|
||||
args[i] = req.session.eval_domain(args[i])
|
||||
for k in kwargs.keys():
|
||||
if isinstance(kwargs[k], common.nonliterals.BaseContext):
|
||||
if isinstance(kwargs[k], nonliterals.BaseContext):
|
||||
kwargs[k] = req.session.eval_context(kwargs[k])
|
||||
elif isinstance(kwargs[k], common.nonliterals.BaseDomain):
|
||||
elif isinstance(kwargs[k], nonliterals.BaseDomain):
|
||||
kwargs[k] = req.session.eval_domain(kwargs[k])
|
||||
|
||||
# Temporary implements future display_name special field for model#read()
|
||||
|
@ -1303,7 +1328,7 @@ class View(openerpweb.Controller):
|
|||
xml = self.transform_view(arch, session, evaluation_context)
|
||||
else:
|
||||
xml = ElementTree.fromstring(arch)
|
||||
fvg['arch'] = common.xml2json.from_elementtree(xml, preserve_whitespaces)
|
||||
fvg['arch'] = from_elementtree(xml, preserve_whitespaces)
|
||||
|
||||
if 'id' in fvg['fields']:
|
||||
# Special case for id's
|
||||
|
@ -1436,12 +1461,12 @@ class SearchView(View):
|
|||
try:
|
||||
parsed_context = parse_context(filter["context"], req.session)
|
||||
filter["context"] = (parsed_context
|
||||
if not isinstance(parsed_context, common.nonliterals.BaseContext)
|
||||
if not isinstance(parsed_context, nonliterals.BaseContext)
|
||||
else req.session.eval_context(parsed_context))
|
||||
|
||||
parsed_domain = parse_domain(filter["domain"], req.session)
|
||||
filter["domain"] = (parsed_domain
|
||||
if not isinstance(parsed_domain, common.nonliterals.BaseDomain)
|
||||
if not isinstance(parsed_domain, nonliterals.BaseDomain)
|
||||
else req.session.eval_domain(parsed_domain))
|
||||
except Exception:
|
||||
logger.exception("Failed to parse custom filter %s in %s",
|
||||
|
@ -1932,7 +1957,7 @@ class Reports(View):
|
|||
|
||||
report_srv = req.session.proxy("report")
|
||||
context = req.session.eval_context(
|
||||
common.nonliterals.CompoundContext(
|
||||
nonliterals.CompoundContext(
|
||||
req.context or {}, action[ "context"]))
|
||||
|
||||
report_data = {}
|
||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
@ -1,6 +1,16 @@
|
|||
API changes from OpenERP Web 6.1 to 7.0
|
||||
=======================================
|
||||
|
||||
Supported browsers
|
||||
------------------
|
||||
|
||||
The OpenERP Web Client supports the following web browsers:
|
||||
|
||||
* Internet Explorer 9+
|
||||
* Google Chrome 22+
|
||||
* Firefox 13+
|
||||
* Any browser using the latest version of Chrome Frame
|
||||
|
||||
DataSet -> Model
|
||||
----------------
|
||||
|
|
@ -6,15 +6,18 @@ import ast
|
|||
import cgi
|
||||
import contextlib
|
||||
import functools
|
||||
import getpass
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import urllib
|
||||
import urlparse
|
||||
import uuid
|
||||
import xmlrpclib
|
||||
|
||||
|
@ -26,18 +29,15 @@ import werkzeug.utils
|
|||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
|
||||
from . import nonliterals
|
||||
from . import session
|
||||
from . import openerplib
|
||||
import urlparse
|
||||
import openerp
|
||||
|
||||
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
||||
'WebRequest', 'JsonRequest', 'HttpRequest']
|
||||
import nonliterals
|
||||
import session
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web RequestHandler
|
||||
# RequestHandler
|
||||
#----------------------------------------------------------
|
||||
class WebRequest(object):
|
||||
""" Parent class for all OpenERP Web request types, mostly deals with
|
||||
|
@ -46,7 +46,6 @@ class WebRequest(object):
|
|||
|
||||
:param request: a wrapped werkzeug Request object
|
||||
:type request: :class:`werkzeug.wrappers.BaseRequest`
|
||||
:param config: configuration object
|
||||
|
||||
.. attribute:: httprequest
|
||||
|
||||
|
@ -58,10 +57,6 @@ class WebRequest(object):
|
|||
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
|
||||
|
@ -85,11 +80,10 @@ class WebRequest(object):
|
|||
|
||||
``bool``, indicates whether the debug mode is active on the client
|
||||
"""
|
||||
def __init__(self, request, config):
|
||||
def __init__(self, request):
|
||||
self.httprequest = request
|
||||
self.httpresponse = None
|
||||
self.httpsession = request.session
|
||||
self.config = config
|
||||
|
||||
def init(self, params):
|
||||
self.params = dict(params)
|
||||
|
@ -98,7 +92,6 @@ class WebRequest(object):
|
|||
self.session = self.httpsession.get(self.session_id)
|
||||
if not self.session:
|
||||
self.httpsession[self.session_id] = self.session = session.OpenERPSession()
|
||||
self.session.config = self.config
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
|
@ -180,7 +173,7 @@ class JsonRequest(WebRequest):
|
|||
_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:
|
||||
except session.AuthenticationError:
|
||||
error = {
|
||||
'code': 100,
|
||||
'message': "OpenERP Session Invalid",
|
||||
|
@ -238,8 +231,8 @@ def jsonrequest(f):
|
|||
beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def json_handler(controller, request, config):
|
||||
return JsonRequest(request, config).dispatch(controller, f)
|
||||
def json_handler(controller, request):
|
||||
return JsonRequest(request).dispatch(controller, f)
|
||||
json_handler.exposed = True
|
||||
return json_handler
|
||||
|
||||
|
@ -325,13 +318,13 @@ def httprequest(f):
|
|||
and ``debug`` keys (which are stripped out beforehand)
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def http_handler(controller, request, config):
|
||||
return HttpRequest(request, config).dispatch(controller, f)
|
||||
def http_handler(controller, request):
|
||||
return HttpRequest(request).dispatch(controller, f)
|
||||
http_handler.exposed = True
|
||||
return http_handler
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web Controller registration with a metaclass
|
||||
# Controller registration with a metaclass
|
||||
#----------------------------------------------------------
|
||||
addons_module = {}
|
||||
addons_manifest = {}
|
||||
|
@ -348,7 +341,7 @@ class Controller(object):
|
|||
__metaclass__ = ControllerType
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web Session context manager
|
||||
# Session context manager
|
||||
#----------------------------------------------------------
|
||||
STORES = {}
|
||||
|
||||
|
@ -419,12 +412,13 @@ def session_context(request, storage_path, session_cookie='httpsessionid'):
|
|||
session_store.save(request.session)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERP Web WSGI Application
|
||||
# WSGI Application
|
||||
#----------------------------------------------------------
|
||||
# Add potentially missing (older ubuntu) font mime types
|
||||
mimetypes.add_type('application/font-woff', '.woff')
|
||||
mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
|
||||
mimetypes.add_type('application/x-font-ttf', '.ttf')
|
||||
|
||||
class DisableCacheMiddleware(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
@ -449,45 +443,23 @@ class DisableCacheMiddleware(object):
|
|||
|
||||
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.config = options
|
||||
|
||||
if not hasattr(self.config, 'connector'):
|
||||
if self.config.backend == 'local':
|
||||
self.config.connector = session.LocalConnector()
|
||||
else:
|
||||
self.config.connector = openerplib.get_connector(
|
||||
hostname=self.config.server_host, port=self.config.server_port)
|
||||
|
||||
def __init__(self):
|
||||
self.httpsession_cookie = 'httpsessionid'
|
||||
self.addons = {}
|
||||
|
||||
static_dirs = self._load_addons()
|
||||
if options.serve_static:
|
||||
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
|
||||
self.dispatch = DisableCacheMiddleware(app)
|
||||
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
|
||||
self.dispatch = DisableCacheMiddleware(app)
|
||||
|
||||
if options.session_storage:
|
||||
if not os.path.exists(options.session_storage):
|
||||
os.mkdir(options.session_storage, 0700)
|
||||
self.session_storage = options.session_storage
|
||||
try:
|
||||
username = getpass.getuser()
|
||||
except Exception:
|
||||
username = "unknown"
|
||||
self.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
||||
|
||||
if not os.path.exists(self.session_storage):
|
||||
os.mkdir(self.session_storage, 0700)
|
||||
_logger.debug('HTTP sessions stored in: %s', self.session_storage)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
|
@ -512,7 +484,7 @@ class Root(object):
|
|||
response = werkzeug.exceptions.NotFound()
|
||||
else:
|
||||
with session_context(request, self.session_storage, self.httpsession_cookie) as session:
|
||||
result = handler( request, self.config)
|
||||
result = handler( request)
|
||||
|
||||
if isinstance(result, basestring):
|
||||
headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]
|
||||
|
@ -531,7 +503,7 @@ class Root(object):
|
|||
static URLs to the corresponding directories
|
||||
"""
|
||||
statics = {}
|
||||
for addons_path in self.config.addons_path:
|
||||
for addons_path in openerp.modules.module.ad_paths:
|
||||
for module in os.listdir(addons_path):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||
|
@ -579,4 +551,7 @@ class Root(object):
|
|||
ps = '/'
|
||||
return None
|
||||
|
||||
def wsgi_postload():
|
||||
openerp.wsgi.register_wsgi_handler(Root())
|
||||
|
||||
# vim:et:ts=4:sw=4:
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
"X-Poedit-Language: Czech\n"
|
||||
|
||||
#. openerp-web
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
"Language: es\n"
|
||||
|
||||
#. openerp-web
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -9,13 +9,13 @@ msgstr ""
|
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-02 14:46+0000\n"
|
||||
"Last-Translator: Erwin <Unknown>\n"
|
||||
"Last-Translator: Erwin van der Ploeg (Endian Solutions) <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -15,8 +15,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web/static/src/js/chrome.js:176
|
||||
|
|
|
@ -4,70 +4,58 @@ import babel
|
|||
import dateutil.relativedelta
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
import sys
|
||||
import xmlrpclib
|
||||
|
||||
import openerplib
|
||||
import openerp
|
||||
|
||||
from . import nonliterals
|
||||
import nonliterals
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# openerplib local connector
|
||||
#----------------------------------------------------------
|
||||
class LibException(Exception):
|
||||
""" Base of all client lib exceptions """
|
||||
def __init__(self,code=None,message=None):
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
||||
class ApplicationError(LibException):
|
||||
""" maps to code: 1, server side: Exception or openerp.exceptions.DeferredException"""
|
||||
|
||||
class Warning(LibException):
|
||||
""" maps to code: 2, server side: openerp.exceptions.Warning"""
|
||||
|
||||
class AccessError(LibException):
|
||||
""" maps to code: 3, server side: openerp.exceptions.AccessError"""
|
||||
|
||||
class AccessDenied(LibException):
|
||||
""" maps to code: 4, server side: openerp.exceptions.AccessDenied"""
|
||||
|
||||
class LocalConnector(openerplib.Connector):
|
||||
"""
|
||||
A type of connector that uses the XMLRPC protocol.
|
||||
"""
|
||||
PROTOCOL = 'local'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def send(self, service_name, method, *args):
|
||||
import openerp
|
||||
import traceback
|
||||
import xmlrpclib
|
||||
code_string = "warning -- %s\n\n%s"
|
||||
try:
|
||||
return openerp.netsvc.dispatch_rpc(service_name, method, args)
|
||||
except openerp.osv.osv.except_osv, e:
|
||||
# TODO change the except to raise LibException instead of their emulated xmlrpc fault
|
||||
raise xmlrpclib.Fault(code_string % (e.name, e.value), '')
|
||||
except openerp.exceptions.Warning, e:
|
||||
raise xmlrpclib.Fault(code_string % ("Warning", e), '')
|
||||
except openerp.exceptions.AccessError, e:
|
||||
raise xmlrpclib.Fault(code_string % ("AccessError", e), '')
|
||||
except openerp.exceptions.AccessDenied, e:
|
||||
raise xmlrpclib.Fault('AccessDenied', str(e))
|
||||
except openerp.exceptions.DeferredException, e:
|
||||
formatted_info = "".join(traceback.format_exception(*e.traceback))
|
||||
raise xmlrpclib.Fault(openerp.tools.ustr(e.message), formatted_info)
|
||||
except Exception, e:
|
||||
formatted_info = "".join(traceback.format_exception(*(sys.exc_info())))
|
||||
raise xmlrpclib.Fault(openerp.tools.exception_to_unicode(e), formatted_info)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# OpenERPSession RPC openerp backend access
|
||||
#----------------------------------------------------------
|
||||
class AuthenticationError(Exception):
|
||||
pass
|
||||
|
||||
class Service(object):
|
||||
def __init__(self, session, service_name):
|
||||
self.session = session
|
||||
self.service_name = service_name
|
||||
|
||||
def __getattr__(self, method):
|
||||
def proxy_method(*args):
|
||||
result = self.session.send(self.service_name, method, *args)
|
||||
return result
|
||||
return proxy_method
|
||||
|
||||
class Model(object):
|
||||
def __init__(self, session, model):
|
||||
self.session = session
|
||||
self.model = model
|
||||
self.proxy = self.session.proxy('object')
|
||||
|
||||
def __getattr__(self, method):
|
||||
def proxy(*args, **kw):
|
||||
result = self.proxy.execute_kw(self.session._db, self.session._uid, self.session._password, self.model, method, args, kw)
|
||||
# reorder read
|
||||
if method == "read":
|
||||
if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
|
||||
index = {}
|
||||
for r in result:
|
||||
index[r['id']] = r
|
||||
result = [index[x] for x in args[0] if x in index]
|
||||
return result
|
||||
return proxy
|
||||
|
||||
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, context=None):
|
||||
record_ids = self.search(domain or [], offset, limit or False, order or False, context or {})
|
||||
if not record_ids: return []
|
||||
records = self.read(record_ids, fields or [], context or {})
|
||||
return records
|
||||
|
||||
class OpenERPSession(object):
|
||||
"""
|
||||
An OpenERP RPC session, a given user can own multiple such sessions
|
||||
|
@ -87,7 +75,6 @@ class OpenERPSession(object):
|
|||
"""
|
||||
def __init__(self):
|
||||
self._creation_time = time.time()
|
||||
self.config = None
|
||||
self._db = False
|
||||
self._uid = False
|
||||
self._login = False
|
||||
|
@ -98,19 +85,27 @@ class OpenERPSession(object):
|
|||
self.domains_store = {}
|
||||
self.jsonp_requests = {} # FIXME use a LRU
|
||||
|
||||
def __getstate__(self):
|
||||
state = dict(self.__dict__)
|
||||
if "config" in state:
|
||||
del state['config']
|
||||
return state
|
||||
|
||||
def build_connection(self):
|
||||
conn = openerplib.Connection(self.config.connector, database=self._db, login=self._login,
|
||||
user_id=self._uid, password=self._password)
|
||||
return conn
|
||||
def send(self, service_name, method, *args):
|
||||
code_string = "warning -- %s\n\n%s"
|
||||
try:
|
||||
return openerp.netsvc.dispatch_rpc(service_name, method, args)
|
||||
except openerp.osv.osv.except_osv, e:
|
||||
raise xmlrpclib.Fault(code_string % (e.name, e.value), '')
|
||||
except openerp.exceptions.Warning, e:
|
||||
raise xmlrpclib.Fault(code_string % ("Warning", e), '')
|
||||
except openerp.exceptions.AccessError, e:
|
||||
raise xmlrpclib.Fault(code_string % ("AccessError", e), '')
|
||||
except openerp.exceptions.AccessDenied, e:
|
||||
raise xmlrpclib.Fault('AccessDenied', str(e))
|
||||
except openerp.exceptions.DeferredException, e:
|
||||
formatted_info = "".join(traceback.format_exception(*e.traceback))
|
||||
raise xmlrpclib.Fault(openerp.tools.ustr(e.message), formatted_info)
|
||||
except Exception, e:
|
||||
formatted_info = "".join(traceback.format_exception(*(sys.exc_info())))
|
||||
raise xmlrpclib.Fault(openerp.tools.exception_to_unicode(e), formatted_info)
|
||||
|
||||
def proxy(self, service):
|
||||
return self.build_connection().get_service(service)
|
||||
return Service(self, service)
|
||||
|
||||
def bind(self, db, uid, login, password):
|
||||
self._db = db
|
||||
|
@ -119,7 +114,6 @@ class OpenERPSession(object):
|
|||
self._password = password
|
||||
|
||||
def authenticate(self, db, login, password, env=None):
|
||||
# TODO use the openerplib API once it exposes authenticate()
|
||||
uid = self.proxy('common').authenticate(db, login, password, env)
|
||||
self.bind(db, uid, login, password)
|
||||
|
||||
|
@ -130,7 +124,12 @@ class OpenERPSession(object):
|
|||
"""
|
||||
Ensures this session is valid (logged into the openerp server)
|
||||
"""
|
||||
self.build_connection().check_login(force)
|
||||
if self._uid and not force:
|
||||
return
|
||||
# TODO use authenticate instead of login
|
||||
uid = self.proxy("common").login(self._db, self._login, self._password)
|
||||
if not uid:
|
||||
raise AuthenticationError("Authentication failure")
|
||||
|
||||
def ensure_valid(self):
|
||||
if self._uid:
|
||||
|
@ -141,7 +140,7 @@ class OpenERPSession(object):
|
|||
|
||||
def execute(self, model, func, *l, **d):
|
||||
self.assert_valid()
|
||||
model = self.build_connection().get_model(model)
|
||||
model = self.model(model)
|
||||
r = getattr(model, func)(*l, **d)
|
||||
return r
|
||||
|
||||
|
@ -157,7 +156,8 @@ class OpenERPSession(object):
|
|||
:type model: str
|
||||
:rtype: a model object
|
||||
"""
|
||||
return self.build_connection().get_model(model)
|
||||
|
||||
return Model(self, model)
|
||||
|
||||
def get_context(self):
|
||||
""" Re-initializes the current user's session context (based on
|
||||
|
@ -167,7 +167,7 @@ class OpenERPSession(object):
|
|||
:returns: the new context
|
||||
"""
|
||||
assert self._uid, "The user needs to be logged-in to initialize his context"
|
||||
self.context = self.build_connection().get_user_context() or {}
|
||||
self.context = self.model('res.users').context_get() or {}
|
||||
self.context['uid'] = self._uid
|
||||
self._fix_lang(self.context)
|
||||
return self.context
|
|
@ -25,6 +25,7 @@
|
|||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.openerp.openerp_webclient_container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
@ -1184,7 +1185,7 @@
|
|||
color: white;
|
||||
padding: 2px 4px;
|
||||
margin: 1px 6px 0 0;
|
||||
border: 1px solid lightgrey;
|
||||
border: 1px solid lightGray;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
|
@ -1209,7 +1210,7 @@
|
|||
transform: scale(1.1);
|
||||
}
|
||||
.openerp .oe_secondary_submenu .oe_active {
|
||||
border-top: 1px solid lightgrey;
|
||||
border-top: 1px solid lightGray;
|
||||
border-bottom: 1px solid #dedede;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2);
|
||||
|
@ -2054,36 +2055,36 @@
|
|||
width: 400px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags div.oe_chatter {
|
||||
.openerp .oe_form div.oe_chatter {
|
||||
min-width: 650px;
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 0 48px;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags div.oe_form_configuration p, .openerp .oe_form header .oe_tags div.oe_form_configuration ul, .openerp .oe_form header .oe_tags div.oe_form_configuration ol {
|
||||
.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol {
|
||||
color: #aaaaaa;
|
||||
max-width: 650px;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags div.oe_form_configuration label {
|
||||
.openerp .oe_form div.oe_form_configuration label {
|
||||
min-width: 150px;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell_label {
|
||||
.openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label {
|
||||
padding: 1px 0;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell div div {
|
||||
.openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div {
|
||||
padding: 1px 0;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer {
|
||||
.openerp .oe_form .oe_subtotal_footer {
|
||||
width: 1% !important;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell {
|
||||
.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell {
|
||||
text-align: right;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell_label {
|
||||
.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell_label {
|
||||
border-right: none;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer .oe_subtotal_footer_separator {
|
||||
.openerp .oe_form .oe_subtotal_footer .oe_subtotal_footer_separator {
|
||||
width: 108px;
|
||||
border-top: 1px solid #cacaca;
|
||||
margin-top: 4px;
|
||||
|
@ -2091,14 +2092,14 @@
|
|||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer label:after {
|
||||
.openerp .oe_form .oe_subtotal_footer label:after {
|
||||
content: ":";
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_subtotal_footer_separator {
|
||||
.openerp .oe_form .oe_subtotal_footer label.oe_subtotal_footer_separator {
|
||||
font-weight: bold !important;
|
||||
padding: 2px 11px 2px 0px !important;
|
||||
}
|
||||
.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_form_label_help {
|
||||
.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help {
|
||||
font-weight: normal;
|
||||
}
|
||||
.openerp .oe_form .oe_form_button {
|
||||
|
@ -2130,7 +2131,7 @@
|
|||
}
|
||||
.openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span {
|
||||
font-size: 80%;
|
||||
color: darkgreen;
|
||||
color: darkGreen;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: -4px;
|
||||
|
|
|
@ -1616,16 +1616,18 @@ $sheet-max-width: 860px
|
|||
ul
|
||||
display: inline-block
|
||||
float: right
|
||||
.oe_button,
|
||||
.oe_button
|
||||
margin: 3px 2px 1px
|
||||
&:first-child
|
||||
margin-left: 6px
|
||||
// }}}
|
||||
// FormView.custom tags and classes {{{
|
||||
.oe_form header .oe_tags
|
||||
margin: 5px 0 0 5px
|
||||
width: 400px
|
||||
padding-bottom: 0
|
||||
.oe_form
|
||||
header
|
||||
.oe_tags
|
||||
margin: 5px 0 0 5px
|
||||
width: 400px
|
||||
padding-bottom: 0
|
||||
div.oe_chatter
|
||||
min-width: 650px
|
||||
max-width: $sheet-max-width
|
||||
|
|
|
@ -70,7 +70,7 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
autoOpen: false,
|
||||
position: [false, 40],
|
||||
buttons: {},
|
||||
beforeClose: function () { self.on_close(); },
|
||||
beforeClose: function () { self.close(); },
|
||||
resizeStop: this.on_resized
|
||||
};
|
||||
for (var f in this) {
|
||||
|
@ -150,16 +150,14 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
var res = this.start();
|
||||
return res;
|
||||
},
|
||||
close: function() {
|
||||
this.$el.dialog('close');
|
||||
},
|
||||
on_close: function() {
|
||||
close: function() {
|
||||
if (this.__tmp_dialog_destroying)
|
||||
return;
|
||||
if (this.dialog_options.destroy_on_close) {
|
||||
this.__tmp_dialog_closing = true;
|
||||
this.destroy();
|
||||
this.__tmp_dialog_closing = undefined;
|
||||
this.trigger("dialog_close");
|
||||
}
|
||||
},
|
||||
on_resized: function() {
|
||||
|
@ -181,7 +179,7 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
});
|
||||
|
||||
instance.web.CrashManager = instance.web.CallbackEnabled.extend({
|
||||
on_rpc_error: function(error) {
|
||||
rpc_error: function(error) {
|
||||
if (error.data.fault_code) {
|
||||
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
|
||||
if (split.length > 1) {
|
||||
|
@ -190,12 +188,12 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
|
|||
}
|
||||
}
|
||||
if (error.code === 200 && error.type) {
|
||||
this.on_managed_error(error);
|
||||
this.show_warning(error);
|
||||
} else {
|
||||
this.on_traceback(error);
|
||||
this.show_error(error);
|
||||
}
|
||||
},
|
||||
on_managed_error: function(error) {
|
||||
show_warning: function(error) {
|
||||
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
|
||||
title: "OpenERP " + _.str.capitalize(error.type),
|
||||
buttons: [
|
||||
|
@ -203,7 +201,7 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
|
|||
]
|
||||
});
|
||||
},
|
||||
on_traceback: function(error) {
|
||||
show_error: function(error) {
|
||||
var self = this;
|
||||
var buttons = {};
|
||||
buttons[_t("Ok")] = function() {
|
||||
|
@ -219,8 +217,8 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
|
|||
}).open();
|
||||
dialog.$el.html(QWeb.render('CrashManager.error', {session: instance.session, error: error}));
|
||||
},
|
||||
on_javascript_exception: function(exception) {
|
||||
this.on_traceback({
|
||||
show_message: function(exception) {
|
||||
this.show_error({
|
||||
type: _t("Client Error"),
|
||||
message: exception,
|
||||
data: {debug: ""}
|
||||
|
@ -236,7 +234,7 @@ instance.web.Loading = instance.web.Widget.extend({
|
|||
this.blocked_ui = false;
|
||||
this.session.on("request", this, this.request_call);
|
||||
this.session.on("response", this, this.response_call);
|
||||
this.session.on("error", this, this.response_call);
|
||||
this.session.on("response_failed", this, this.response_call);
|
||||
},
|
||||
destroy: function() {
|
||||
this.on_rpc_event(-this.count);
|
||||
|
@ -627,6 +625,7 @@ instance.web.Reload = function(parent, params) {
|
|||
hash = "#menu_id=" + menu_id;
|
||||
}
|
||||
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
|
||||
window.onerror = function() {};
|
||||
window.location = url;
|
||||
};
|
||||
instance.web.client_actions.add("reload", "instance.web.Reload");
|
||||
|
@ -878,9 +877,8 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
};
|
||||
this.update_promise = this.update_promise.pipe(fct, fct);
|
||||
},
|
||||
on_action: function() {
|
||||
},
|
||||
on_menu_logout: function() {
|
||||
this.trigger('user_logout');
|
||||
},
|
||||
on_menu_settings: function() {
|
||||
var self = this;
|
||||
|
@ -897,8 +895,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
var $help = $(QWeb.render("UserMenu.about", {version_info: res}));
|
||||
$help.find('a.oe_activate_debug_mode').click(function (e) {
|
||||
e.preventDefault();
|
||||
window.location = $.param.querystring(
|
||||
window.location.href, 'debug');
|
||||
window.location = $.param.querystring( window.location.href, 'debug');
|
||||
});
|
||||
instance.web.dialog($help, {autoOpen: true,
|
||||
modal: true, width: 507, height: 290, resizable: false, title: _t("About")});
|
||||
|
@ -957,7 +954,7 @@ instance.web.Client = instance.web.Widget.extend({
|
|||
show_common: function() {
|
||||
var self = this;
|
||||
this.crashmanager = new instance.web.CrashManager();
|
||||
instance.session.on_rpc_error.add(this.crashmanager.on_rpc_error);
|
||||
instance.session.on('error', this.crashmanager, this.crashmanager.rpc_error);
|
||||
self.notification = new instance.web.Notification(this);
|
||||
self.notification.appendTo(self.$el);
|
||||
self.loading = new instance.web.Loading(self);
|
||||
|
@ -1007,7 +1004,7 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
var self = this;
|
||||
this._super();
|
||||
window.onerror = function (message, file, line) {
|
||||
self.crashmanager.on_traceback({
|
||||
self.crashmanager.show_error({
|
||||
type: _t("Client Error"),
|
||||
message: message,
|
||||
data: {debug: file + ':' + line}
|
||||
|
@ -1016,20 +1013,16 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
},
|
||||
show_login: function() {
|
||||
this.toggle_bars(false);
|
||||
|
||||
var action = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'login'
|
||||
};
|
||||
|
||||
var state = $.bbq.getState(true);
|
||||
if (state.action === "login") {
|
||||
action.params = state;
|
||||
}
|
||||
var action = {
|
||||
type: 'ir.actions.client',
|
||||
tag: 'login',
|
||||
_push_me: false,
|
||||
};
|
||||
|
||||
this.action_manager.do_action(action);
|
||||
this.action_manager.inner_widget.on('login_successful', this, function() {
|
||||
this.do_push_state(state);
|
||||
this._current_state = null; // ensure the state will be loaded
|
||||
this.show_application(); // will load the state we just pushed
|
||||
});
|
||||
},
|
||||
|
@ -1041,8 +1034,7 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
self.menu.on('menu_click', this, this.on_menu_action);
|
||||
self.user_menu = new instance.web.UserMenu(self);
|
||||
self.user_menu.replace(this.$el.find('.oe_user_menu_placeholder'));
|
||||
self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
|
||||
self.user_menu.on_action.add(this.proxy('on_menu_action'));
|
||||
self.user_menu.on('user_logout', self, self.on_logout);
|
||||
self.user_menu.do_update();
|
||||
self.bind_hashchange();
|
||||
self.set_title();
|
||||
|
@ -1105,6 +1097,7 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
});
|
||||
});
|
||||
} else {
|
||||
state._push_me = false; // no need to push state back...
|
||||
this.action_manager.do_load_state(state, !!this._current_state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -980,7 +980,8 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
|
|||
triggers: {
|
||||
'request': 'Request sent',
|
||||
'response': 'Response received',
|
||||
'error': 'HTTP Error response or timeout received',
|
||||
'response_failed': 'HTTP Error response or timeout received',
|
||||
'error': 'The received response is an JSON-RPC error',
|
||||
},
|
||||
/**
|
||||
* @constructs instance.web.JsonRPC
|
||||
|
@ -1336,7 +1337,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
|
|||
}
|
||||
},
|
||||
function(jqXHR, textStatus, errorThrown) {
|
||||
self.trigger('error');
|
||||
self.trigger('response_failed', jqXHR);
|
||||
var error = {
|
||||
code: -32098,
|
||||
message: "XmlHttpRequestError " + errorThrown,
|
||||
|
@ -1348,7 +1349,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
|
|||
deferred.fail(function() {
|
||||
deferred.fail(function(error, event) {
|
||||
if (!event.isDefaultPrevented()) {
|
||||
self.on_rpc_error(error, event);
|
||||
self.trigger('error', error, event);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1434,13 +1435,15 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
|
|||
return deferred;
|
||||
}
|
||||
},
|
||||
on_rpc_error: function(error) {
|
||||
},
|
||||
get_url: function (file) {
|
||||
return this.prefix + file;
|
||||
},
|
||||
});
|
||||
|
||||
instance.web.py_eval = function(expr, context) {
|
||||
return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
|
|
|
@ -280,7 +280,8 @@ instance.web.Model = instance.web.Class.extend({
|
|||
kwargs = args;
|
||||
args = [];
|
||||
}
|
||||
return instance.session.rpc('/web/dataset/call_kw', {
|
||||
var debug = instance.session.debug ? '/'+this.name+':'+method : '';
|
||||
return instance.session.rpc('/web/dataset/call_kw' + debug, {
|
||||
model: this.name,
|
||||
method: method,
|
||||
args: args,
|
||||
|
@ -567,7 +568,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
|
|||
*/
|
||||
write: function (id, data, options) {
|
||||
options = options || {};
|
||||
return this._model.call('write', [[id], data], {context: this._model.context(options.context)});
|
||||
return this._model.call('write', [[id], data], {context: this._model.context(options.context)}).then(this.trigger('dataset_changed', id, data, options));
|
||||
},
|
||||
/**
|
||||
* Deletes an existing record from the database
|
||||
|
@ -575,7 +576,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
|
|||
* @param {Number|String} ids identifier of the record to delete
|
||||
*/
|
||||
unlink: function(ids) {
|
||||
return this._model.call('unlink', [ids], {context: this._model.context()});
|
||||
return this._model.call('unlink', [ids], {context: this._model.context()}).then(this.trigger('dataset_changed', ids));
|
||||
},
|
||||
/**
|
||||
* Calls an arbitrary RPC method
|
||||
|
@ -690,6 +691,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
|
|||
|
||||
instance.web.DataSetStatic = instance.web.DataSet.extend({
|
||||
init: function(parent, model, context, ids) {
|
||||
var self = this;
|
||||
this._super(parent, model, context);
|
||||
// all local records
|
||||
this.ids = ids || [];
|
||||
|
@ -711,12 +713,10 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({
|
|||
}
|
||||
},
|
||||
unlink: function(ids) {
|
||||
this.on_unlink(ids);
|
||||
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
||||
this.trigger('unlink', ids);
|
||||
return $.Deferred().resolve({result: true});
|
||||
},
|
||||
on_unlink: function(ids) {
|
||||
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
||||
}
|
||||
});
|
||||
|
||||
instance.web.DataSetSearch = instance.web.DataSet.extend({
|
||||
|
@ -778,6 +778,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
|
|||
self.index = self.index <= self.ids.length - 1 ?
|
||||
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
|
||||
}
|
||||
this.trigger("dataset_changed", ids, callback, error_callback);
|
||||
});
|
||||
},
|
||||
size: function () {
|
||||
|
@ -834,7 +835,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
}
|
||||
$.extend(cached.values, record.values);
|
||||
if (dirty)
|
||||
this.on_change();
|
||||
this.trigger("dataset_changed", id, data, options);
|
||||
return $.Deferred().resolve(true).promise();
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
|
@ -848,7 +849,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);});
|
||||
this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);});
|
||||
this.set_ids(_.without.apply(_, [this.ids].concat(ids)));
|
||||
this.on_change();
|
||||
this.trigger("dataset_changed", ids, callback, error_callback);
|
||||
return $.async_when({result: true}).then(callback);
|
||||
},
|
||||
reset_ids: function(ids) {
|
||||
|
@ -859,8 +860,6 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
this.cache = [];
|
||||
this.delete_all = false;
|
||||
},
|
||||
on_change: function() {
|
||||
},
|
||||
read_ids: function (ids, fields, options) {
|
||||
var self = this;
|
||||
var to_get = [];
|
||||
|
@ -947,7 +946,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
},
|
||||
alter_ids: function(n_ids) {
|
||||
this._super(n_ids);
|
||||
this.on_change();
|
||||
this.trigger("dataset_changed", n_ids);
|
||||
},
|
||||
});
|
||||
instance.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
|
||||
|
|
|
@ -173,10 +173,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
} else {
|
||||
this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
|
||||
}
|
||||
this.$buttons.on('click','.oe_form_button_create',this.on_button_create);
|
||||
this.$buttons.on('click','.oe_form_button_edit',this.on_button_edit);
|
||||
this.$buttons.on('click','.oe_form_button_save',this.on_button_save);
|
||||
this.$buttons.on('click','.oe_form_button_cancel',this.on_button_cancel);
|
||||
this.$buttons.on('click', '.oe_form_button_create', this.on_button_create);
|
||||
this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit);
|
||||
this.$buttons.on('click', '.oe_form_button_save', this.on_button_save);
|
||||
this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel);
|
||||
|
||||
this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar');
|
||||
if (!this.sidebar && this.options.$sidebar) {
|
||||
|
@ -431,7 +431,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
do_notify_change: function() {
|
||||
this.$el.add(this.$buttons).addClass('oe_form_dirty');
|
||||
},
|
||||
on_pager_action: function(action) {
|
||||
execute_pager_action: function(action) {
|
||||
if (this.can_be_discarded()) {
|
||||
switch (action) {
|
||||
case 'first':
|
||||
|
@ -448,6 +448,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
break;
|
||||
}
|
||||
this.reload();
|
||||
this.trigger('pager_action_executed');
|
||||
}
|
||||
},
|
||||
init_pager: function() {
|
||||
|
@ -464,7 +465,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
this.$pager.on('click','a[data-pager-action]',function() {
|
||||
var action = $(this).data('pager-action');
|
||||
self.on_pager_action(action);
|
||||
self.execute_pager_action(action);
|
||||
});
|
||||
this.do_update_pager();
|
||||
},
|
||||
|
@ -615,7 +616,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return self.on_processed_onchange(response, processed);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
instance.webclient.crashmanager.on_javascript_exception(e);
|
||||
instance.webclient.crashmanager.show_message(e);
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
});
|
||||
|
@ -650,7 +651,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return $.Deferred().resolve();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
instance.webclient.crashmanager.on_javascript_exception(e);
|
||||
instance.webclient.crashmanager.show_message(e);
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
},
|
||||
|
@ -747,7 +748,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
},
|
||||
on_button_save: function() {
|
||||
var self = this;
|
||||
return this.do_save().then(function(result) {
|
||||
return this.save().then(function(result) {
|
||||
self.trigger("save", result);
|
||||
self.to_view_mode();
|
||||
});
|
||||
},
|
||||
|
@ -760,6 +762,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
this.trigger('load_record', this.datarecord);
|
||||
}
|
||||
}
|
||||
this.trigger('on_button_cancel');
|
||||
return false;
|
||||
},
|
||||
on_button_new: function() {
|
||||
|
@ -783,7 +786,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
var def = $.Deferred();
|
||||
$.when(this.has_been_loaded).then(function() {
|
||||
self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) {
|
||||
return self.on_created({ result : new_id });
|
||||
return self.record_created(new_id);
|
||||
}).then(function() {
|
||||
return self.to_edit_mode();
|
||||
}).then(function() {
|
||||
|
@ -798,7 +801,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
$.when(this.has_been_loaded).then(function() {
|
||||
if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
|
||||
self.dataset.unlink([self.datarecord.id]).then(function() {
|
||||
self.on_pager_action('next');
|
||||
self.execute_pager_action('next');
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
|
@ -823,11 +826,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
* record or saving an existing one depending on whether the record
|
||||
* already has an id property.
|
||||
*
|
||||
* @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new
|
||||
* @param {Boolean} [prepend_on_create=false] if ``save`` creates a new
|
||||
* record, should that record be inserted at the start of the dataset (by
|
||||
* default, records are added at the end)
|
||||
*/
|
||||
do_save: function(prepend_on_create) {
|
||||
save: function(prepend_on_create) {
|
||||
var self = this;
|
||||
return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
|
||||
try {
|
||||
|
@ -860,7 +863,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
if (!self.datarecord.id) {
|
||||
// Creation save
|
||||
save_deferral = self.dataset.create(values).pipe(function(r) {
|
||||
return self.on_created(r, prepend_on_create);
|
||||
return self.record_created(r, prepend_on_create);
|
||||
}, null);
|
||||
} else if (_.isEmpty(values) && ! self.force_dirty) {
|
||||
// Not dirty, noop save
|
||||
|
@ -869,7 +872,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
self.force_dirty = false;
|
||||
// Write save
|
||||
save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
|
||||
return self.on_saved(r);
|
||||
return self.record_saved(r);
|
||||
}, null);
|
||||
}
|
||||
return save_deferral;
|
||||
|
@ -896,12 +899,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
*
|
||||
* @param {Object} r result of the write function.
|
||||
*/
|
||||
on_saved: function(r) {
|
||||
record_saved: function(r) {
|
||||
var self = this;
|
||||
if (!r) {
|
||||
// should not happen in the server, but may happen for internal purpose
|
||||
this.trigger('record_saved', r);
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
return $.when(this.reload()).pipe(function () {
|
||||
self.trigger('record_saved', r);
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
@ -919,9 +925,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
* @param {Boolean} [prepend_on_create=false] adds the newly created record
|
||||
* at the beginning of the dataset instead of the end
|
||||
*/
|
||||
on_created: function(r, prepend_on_create) {
|
||||
record_created: function(r, prepend_on_create) {
|
||||
var self = this;
|
||||
if (!r) {
|
||||
// should not happen in the server, but may happen for internal purpose
|
||||
this.trigger('record_created', r);
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
this.datarecord.id = r;
|
||||
|
@ -935,9 +943,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
this.do_update_pager();
|
||||
if (this.sidebar) {
|
||||
this.sidebar.do_attachement_update(this.dataset, this.datarecord.id);
|
||||
}
|
||||
}
|
||||
//openerp.log("The record has been created with id #" + this.datarecord.id);
|
||||
return $.when(this.reload()).pipe(function () {
|
||||
self.trigger('record_created', r);
|
||||
return _.extend(r, {created: true});
|
||||
});
|
||||
}
|
||||
|
@ -949,7 +958,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
var self = this;
|
||||
return this.reload_mutex.exec(function() {
|
||||
if (self.dataset.index == null) {
|
||||
self.do_prev_view();
|
||||
self.trigger("previous_view");
|
||||
return $.Deferred().reject().promise();
|
||||
}
|
||||
if (self.dataset.index == null || self.dataset.index < 0) {
|
||||
|
@ -993,7 +1002,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
},
|
||||
recursive_save: function() {
|
||||
var self = this;
|
||||
return $.when(this.do_save()).pipe(function(res) {
|
||||
return $.when(this.save()).pipe(function(res) {
|
||||
if (self.dataset.parent_view)
|
||||
return self.dataset.parent_view.recursive_save();
|
||||
});
|
||||
|
@ -1024,7 +1033,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return true;
|
||||
},
|
||||
sidebar_context: function () {
|
||||
return this.do_save().pipe(_.bind(function() {return this.get_fields_values();}, this));
|
||||
return this.save().pipe(_.bind(function() {return this.get_fields_values();}, this));
|
||||
},
|
||||
open_defaults_dialog: function () {
|
||||
var self = this;
|
||||
|
@ -1089,7 +1098,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
field_to_set,
|
||||
self.fields[field_to_set].get_value(),
|
||||
all_users,
|
||||
false,
|
||||
true,
|
||||
condition || false
|
||||
]).then(function () { d.close(); });
|
||||
}}
|
||||
|
@ -1629,13 +1638,14 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({
|
|||
return this;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
|
||||
pager: false
|
||||
});
|
||||
this.form.appendTo(this.$el);
|
||||
this.form.on_created.add_last(this.on_form_dialog_saved);
|
||||
this.form.on_saved.add_last(this.on_form_dialog_saved);
|
||||
this.form.on('record_created', self, this.on_form_dialog_saved);
|
||||
this.form.on('record_saved', this, this.on_form_dialog_saved);
|
||||
return this;
|
||||
},
|
||||
select_id: function(id) {
|
||||
|
@ -1652,6 +1662,8 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({
|
|||
});
|
||||
|
||||
instance.web.form.compute_domain = function(expr, fields) {
|
||||
if (! (expr instanceof Array))
|
||||
return !! expr;
|
||||
var stack = [];
|
||||
for (var i = expr.length - 1; i >= 0; i--) {
|
||||
var ex = expr[i];
|
||||
|
@ -2079,11 +2091,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
this.field = this.field_manager.get_field_desc(this.name);
|
||||
this.widget = this.node.attrs.widget;
|
||||
this.string = this.node.attrs.string || this.field.string || this.name;
|
||||
try {
|
||||
this.options = JSON.parse(this.node.attrs.options || '{}');
|
||||
} catch (e) {
|
||||
throw new Error(_.str.sprintf(_t("Widget options for field '%s' are not valid JSON."), this.name));
|
||||
}
|
||||
this.options = instance.web.py_eval(this.node.attrs.options || '{}');
|
||||
this.set({'value': false});
|
||||
|
||||
this.on("change:value", this, function() {
|
||||
|
@ -2371,7 +2379,10 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
|||
var self = this;
|
||||
this.$input = this.$el.find('input.oe_datepicker_master');
|
||||
this.$input_picker = this.$el.find('input.oe_datepicker_container');
|
||||
this.$input.change(this.on_change);
|
||||
this.$input.change(function(){
|
||||
self.change_datetime();
|
||||
});
|
||||
|
||||
this.picker({
|
||||
onClose: this.on_picker_select,
|
||||
onSelect: this.on_picker_select,
|
||||
|
@ -2439,9 +2450,10 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
|||
format_client: function(v) {
|
||||
return instance.web.format_value(v, {"widget": this.type_of_date});
|
||||
},
|
||||
on_change: function() {
|
||||
change_datetime: function() {
|
||||
if (this.is_valid_()) {
|
||||
this.set_value_from_ui_();
|
||||
this.trigger("datetime_changed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2465,7 +2477,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
|
|||
initialize_content: function() {
|
||||
if (!this.get("effective_readonly")) {
|
||||
this.datewidget = this.build_widget();
|
||||
this.datewidget.on_change.add_last(_.bind(function() {
|
||||
this.datewidget.on('datetime_changed', this, _.bind(function() {
|
||||
this.set({'value': this.datewidget.get_value()});
|
||||
}, this));
|
||||
this.datewidget.appendTo(this.$el);
|
||||
|
@ -2869,7 +2881,7 @@ instance.web.form.CompletionFieldMixin = {
|
|||
self.build_domain(),
|
||||
new instance.web.CompoundContext(self.build_context(), context || {})
|
||||
);
|
||||
pop.on_select_elements.add(function(element_ids) {
|
||||
pop.on("elements_selected", self, function(element_ids) {
|
||||
self.add_id(element_ids[0]);
|
||||
self.focus();
|
||||
});
|
||||
|
@ -3276,7 +3288,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
this.dataset.o2m = this;
|
||||
this.dataset.parent_view = this.view;
|
||||
this.dataset.child_name = this.name;
|
||||
this.dataset.on_change.add_last(function() {
|
||||
this.dataset.on('dataset_changed', this, function() {
|
||||
self.trigger_on_change();
|
||||
});
|
||||
|
||||
|
@ -3308,11 +3320,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
var views = [];
|
||||
_.each(modes, function(mode) {
|
||||
if (! _.include(["list", "tree", "graph", "kanban"], mode)) {
|
||||
try {
|
||||
throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode));
|
||||
} catch(e) {
|
||||
instance.webclient.crashmanager.on_javascript_exception(e)
|
||||
}
|
||||
throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode));
|
||||
}
|
||||
var view = {
|
||||
view_id: false,
|
||||
|
@ -3368,7 +3376,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
var def = $.Deferred().then(function() {
|
||||
self.initial_is_loaded.resolve();
|
||||
});
|
||||
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
|
||||
this.viewmanager.on("controller_inited", self, function(view_type, controller) {
|
||||
controller.o2m = self;
|
||||
if (view_type == "list") {
|
||||
if (self.get("effective_readonly")) {
|
||||
|
@ -3383,9 +3391,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
controller.on("load_record", self, function(){
|
||||
once.resolve();
|
||||
});
|
||||
controller.on_pager_action.add_first(function() {
|
||||
self.save_any_view();
|
||||
});
|
||||
controller.on('pager_action_executed',self,self.save_any_view);
|
||||
} else if (view_type == "graph") {
|
||||
self.reload_current_view()
|
||||
}
|
||||
|
@ -3520,7 +3526,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
if (!view.is_initialized.isResolved()) {
|
||||
return false;
|
||||
}
|
||||
var res = $.when(view.do_save());
|
||||
var res = $.when(view.save());
|
||||
if (!res.isResolved() && !res.isRejected()) {
|
||||
console.warn("Asynchronous get_value() is not supported in form view.");
|
||||
}
|
||||
|
@ -3565,7 +3571,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
|
|||
});
|
||||
this.__ignore_blur = false;
|
||||
},
|
||||
do_switch_view: function(mode, unused) {
|
||||
switch_mode: function(mode, unused) {
|
||||
if (mode !== 'form') {
|
||||
return this._super(mode, unused);
|
||||
}
|
||||
|
@ -3577,7 +3583,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
|
|||
create_function: function(data) {
|
||||
return self.o2m.dataset.create(data).then(function(r) {
|
||||
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
|
||||
self.o2m.dataset.on_change();
|
||||
self.o2m.dataset.trigger("dataset_changed", r);
|
||||
});
|
||||
},
|
||||
write_function: function(id, data, options) {
|
||||
|
@ -3594,7 +3600,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
|
|||
form_view_options: {'not_interactible_on_create':true},
|
||||
readonly: self.o2m.get("effective_readonly")
|
||||
});
|
||||
pop.on_select_elements.add_last(function() {
|
||||
pop.on("elements_selected", self, function() {
|
||||
self.o2m.reload_current_view();
|
||||
});
|
||||
},
|
||||
|
@ -3667,7 +3673,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
create_function: function(data, callback, error_callback) {
|
||||
return self.o2m.dataset.create(data).then(function(r) {
|
||||
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
|
||||
self.o2m.dataset.on_change();
|
||||
self.o2m.dataset.trigger("dataset_changed", r);
|
||||
}).then(callback, error_callback);
|
||||
},
|
||||
read_function: function() {
|
||||
|
@ -3680,7 +3686,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
self.o2m.build_domain(),
|
||||
self.o2m.build_context()
|
||||
);
|
||||
pop.on_select_elements.add_last(function() {
|
||||
pop.on("elements_selected", self, function() {
|
||||
self.o2m.reload_current_view();
|
||||
});
|
||||
}
|
||||
|
@ -3716,7 +3722,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
var self = this;
|
||||
this.ensure_saved().pipe(function () {
|
||||
if (parent_form)
|
||||
return parent_form.do_save();
|
||||
return parent_form.save();
|
||||
else
|
||||
return $.when();
|
||||
}).then(function () {
|
||||
|
@ -3819,7 +3825,7 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({
|
|||
colspan: columns,
|
||||
'class': 'oe_form_field_one2many_list_row_add'
|
||||
}).append(
|
||||
$('<a>', {href: '#'}).text(_t("Add a row"))
|
||||
$('<a>', {href: '#'}).text(_t("Add an item"))
|
||||
.mousedown(function () {
|
||||
// FIXME: needs to be an official API somehow
|
||||
if (self.view.editor.is_editing()) {
|
||||
|
@ -3855,7 +3861,7 @@ instance.web.form.One2ManyFormView = instance.web.FormView.extend({
|
|||
this._super(data);
|
||||
var self = this;
|
||||
this.$buttons.find('button.oe_form_button_create').click(function() {
|
||||
self.do_save().then(self.on_button_new);
|
||||
self.save().then(self.on_button_new);
|
||||
});
|
||||
},
|
||||
do_notify_change: function() {
|
||||
|
@ -4022,7 +4028,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
|
|||
|
||||
this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
|
||||
this.dataset.m2m = this;
|
||||
this.dataset.on_unlink.add_last(function(ids) {
|
||||
this.dataset.on('unlink', self, function(ids) {
|
||||
self.dataset_changed();
|
||||
});
|
||||
|
||||
|
@ -4117,7 +4123,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
|
|||
this.m2m_field.build_context()
|
||||
);
|
||||
var self = this;
|
||||
pop.on_select_elements.add(function(element_ids) {
|
||||
pop.on("elements_selected", self, function(element_ids) {
|
||||
_.each(element_ids, function(one_id) {
|
||||
if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
|
||||
self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
|
||||
|
@ -4155,7 +4161,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
|
|||
|
||||
this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
|
||||
this.dataset.m2m = this;
|
||||
this.dataset.on_unlink.add_last(function(ids) {
|
||||
this.dataset.on('unlink', self, function(ids) {
|
||||
self.dataset_changed();
|
||||
});
|
||||
|
||||
|
@ -4203,7 +4209,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
|
|||
self.initial_is_loaded.resolve();
|
||||
loaded.resolve();
|
||||
});
|
||||
this.kanban_view.do_switch_view.add_last(_.bind(this.open_popup, this));
|
||||
this.kanban_view.on('switch_mode', this, this.open_popup);
|
||||
$.async_when().then(function () {
|
||||
self.kanban_view.appendTo(self.$el);
|
||||
});
|
||||
|
@ -4232,7 +4238,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
|
|||
new instance.web.CompoundDomain(this.build_domain(), ["!", ["id", "in", this.dataset.ids]]),
|
||||
this.build_context()
|
||||
);
|
||||
pop.on_select_elements.add(function(element_ids) {
|
||||
pop.on("elements_selected", self, function(element_ids) {
|
||||
_.each(element_ids, function(one_id) {
|
||||
if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
|
||||
self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
|
||||
|
@ -4431,7 +4437,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
|
|||
}));
|
||||
var $snbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save-new");
|
||||
$snbutton.click(function() {
|
||||
$.when(self.view_form.do_save()).then(function() {
|
||||
$.when(self.view_form.save()).then(function() {
|
||||
self.view_form.reload_mutex.exec(function() {
|
||||
self.view_form.on_button_new();
|
||||
});
|
||||
|
@ -4439,7 +4445,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
|
|||
});
|
||||
var $sbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save");
|
||||
$sbutton.click(function() {
|
||||
$.when(self.view_form.do_save()).then(function() {
|
||||
$.when(self.view_form.save()).then(function() {
|
||||
self.view_form.reload_mutex.exec(function() {
|
||||
self.check_exit();
|
||||
});
|
||||
|
@ -4452,11 +4458,12 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
|
|||
self.view_form.do_show();
|
||||
});
|
||||
},
|
||||
on_select_elements: function(element_ids) {
|
||||
select_elements: function(element_ids) {
|
||||
this.trigger("elements_selected", element_ids);
|
||||
},
|
||||
check_exit: function(no_destroy) {
|
||||
if (this.created_elements.length > 0) {
|
||||
this.on_select_elements(this.created_elements);
|
||||
this.select_elements(this.created_elements);
|
||||
this.created_elements = [];
|
||||
}
|
||||
this.destroy();
|
||||
|
@ -4567,7 +4574,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
|
|||
});
|
||||
var $sbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-select");
|
||||
$sbutton.click(function() {
|
||||
self.on_select_elements(self.selected_ids);
|
||||
self.select_elements(self.selected_ids);
|
||||
self.destroy();
|
||||
});
|
||||
});
|
||||
|
@ -4609,7 +4616,7 @@ instance.web.form.SelectCreateListView = instance.web.ListView.extend({
|
|||
this.popup.new_object();
|
||||
},
|
||||
select_record: function(index) {
|
||||
this.popup.on_select_elements([this.dataset.ids[index]]);
|
||||
this.popup.select_elements([this.dataset.ids[index]]);
|
||||
this.popup.destroy();
|
||||
},
|
||||
do_select: function(ids, records) {
|
||||
|
@ -4637,6 +4644,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
|
|||
destroy_content: function() {
|
||||
if (this.fm) {
|
||||
this.fm.destroy();
|
||||
this.fm = undefined;
|
||||
}
|
||||
},
|
||||
initialize_content: function() {
|
||||
|
@ -4658,9 +4666,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
|
|||
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
|
||||
}});
|
||||
this.selection.on("change:value", this, this.on_selection_changed);
|
||||
this.selection.setElement(this.$(".oe_form_view_reference_selection"));
|
||||
this.selection.renderElement();
|
||||
this.selection.start();
|
||||
this.selection.appendTo(this.$(".oe_form_view_reference_selection"));
|
||||
this.selection
|
||||
.on('focused', null, function () {self.trigger('focused')})
|
||||
.on('blurred', null, function () {self.trigger('blurred')});
|
||||
|
@ -4670,9 +4676,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
|
|||
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
|
||||
}});
|
||||
this.m2o.on("change:value", this, this.data_changed);
|
||||
this.m2o.setElement(this.$(".oe_form_view_reference_m2o"));
|
||||
this.m2o.renderElement();
|
||||
this.m2o.start();
|
||||
this.m2o.appendTo(this.$(".oe_form_view_reference_m2o"));
|
||||
this.m2o
|
||||
.on('focused', null, function () {self.trigger('focused')})
|
||||
.on('blurred', null, function () {self.trigger('blurred')});
|
||||
|
@ -4816,9 +4820,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
|
|||
this._super();
|
||||
if (this.get("effective_readonly")) {
|
||||
var self = this;
|
||||
this.$el.find('a').click(function() {
|
||||
this.$el.find('a').click(function(ev) {
|
||||
if (self.get('value')) {
|
||||
self.on_save_as();
|
||||
self.on_save_as(ev);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -1321,7 +1321,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
} catch (e) {
|
||||
group_label = row_data[group_column.id].value;
|
||||
}
|
||||
$group_column.text(_.str.sprintf("%s (%d)",
|
||||
$group_column.html(_.str.sprintf("%s (%d)",
|
||||
group_label, group.length));
|
||||
|
||||
if (group.length && group.openable) {
|
||||
|
|
|
@ -724,7 +724,7 @@ openerp.web.list_editable = function (instance) {
|
|||
save: function () {
|
||||
var self = this;
|
||||
return this.form
|
||||
.do_save(this.delegate.prepends_on_create())
|
||||
.save(this.delegate.prepends_on_create())
|
||||
.pipe(function (result) {
|
||||
var created = result.created && !self.record.id;
|
||||
if (created) {
|
||||
|
|
|
@ -164,6 +164,10 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
state = state || {};
|
||||
if (this.getParent() && this.getParent().do_push_state) {
|
||||
if (this.inner_action) {
|
||||
if (this.inner_action._push_me === false) {
|
||||
// this action has been explicitly marked as not pushable
|
||||
return;
|
||||
}
|
||||
state['title'] = this.inner_action.name;
|
||||
if(this.inner_action.type == 'ir.actions.act_window') {
|
||||
state['model'] = this.inner_action.res_model;
|
||||
|
@ -172,7 +176,13 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
state['action'] = this.inner_action.id;
|
||||
} else if (this.inner_action.type == 'ir.actions.client') {
|
||||
state['action'] = this.inner_action.tag;
|
||||
//state = _.extend(this.inner_action.params || {}, state);
|
||||
var params = {};
|
||||
_.each(this.inner_action.params, function(v, k) {
|
||||
if(_.isString(v) || _.isNumber(v)) {
|
||||
params[k] = v;
|
||||
}
|
||||
});
|
||||
state = _.extend(params || {}, state);
|
||||
}
|
||||
}
|
||||
if(!this.dialog) {
|
||||
|
@ -286,8 +296,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
buttons: { "Close": function() { $(this).dialog("close"); }},
|
||||
dialogClass: executor.klass
|
||||
});
|
||||
if(on_close)
|
||||
this.dialog.on_close.add(on_close);
|
||||
if (on_close) {
|
||||
this.dialog.on("dialog_close", this, on_close);
|
||||
}
|
||||
} else {
|
||||
this.dialog_widget.destroy();
|
||||
}
|
||||
|
@ -525,19 +536,19 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
|||
controller.set_embedded_view(view.embedded_view);
|
||||
}
|
||||
controller.on('switch_mode', self, this.switch_mode);
|
||||
|
||||
controller.do_prev_view.add_last(this.on_prev_view);
|
||||
controller.on('previous_view', self, this.prev_view);
|
||||
|
||||
var container = this.$el.find(".oe_view_manager_view_" + view_type);
|
||||
var view_promise = controller.appendTo(container);
|
||||
this.views[view_type].controller = controller;
|
||||
this.views[view_type].deferred.resolve(view_type);
|
||||
return $.when(view_promise).then(function() {
|
||||
self.on_controller_inited(view_type, controller);
|
||||
if (self.searchview
|
||||
&& self.flags.auto_search
|
||||
&& view.controller.searchable !== false) {
|
||||
self.searchview.ready.then(self.searchview.do_search);
|
||||
}
|
||||
self.trigger("controller_inited",view_type,controller);
|
||||
});
|
||||
},
|
||||
set_title: function(title) {
|
||||
|
@ -602,7 +613,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
|||
* @param {String} [options.default=null] view to switch to if no previous view
|
||||
* @returns {$.Deferred} switching end signal
|
||||
*/
|
||||
on_prev_view: function (options) {
|
||||
prev_view: function (options) {
|
||||
options = options || {};
|
||||
var current_view = this.views_history.pop();
|
||||
var previous_view = this.views_history[this.views_history.length - 1] || options['default'];
|
||||
|
@ -652,14 +663,6 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
|||
controller.do_search(results.domain, results.context, groupby || []);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Event launched when a controller has been inited.
|
||||
*
|
||||
* @param {String} view_type type of view
|
||||
* @param {String} view the inited controller
|
||||
*/
|
||||
on_controller_inited: function(view_type, view) {
|
||||
},
|
||||
/**
|
||||
* Called when one of the view want to execute an action
|
||||
*/
|
||||
|
@ -1266,8 +1269,7 @@ instance.web.View = instance.web.Widget.extend({
|
|||
* @param {Boolean} [options.created=false] resource was created
|
||||
* @param {String} [options.default=null] view to switch to if no previous view
|
||||
*/
|
||||
do_prev_view: function (options) {
|
||||
},
|
||||
|
||||
do_search: function(view) {
|
||||
},
|
||||
on_sidebar_export: function() {
|
||||
|
|
|
@ -548,7 +548,7 @@
|
|||
<a class="oe_sidebar_action_a" t-att-title="item.title" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url" target="_blank">
|
||||
<t t-raw="item.label"/>
|
||||
</a>
|
||||
<a t-if="section.name == 'files'" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">x</a>
|
||||
<a t-if="section.name == 'files' and !item.callback" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">x</a>
|
||||
</li>
|
||||
<li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
|
||||
<t t-call="HiddenInputFile">
|
||||
|
@ -1537,7 +1537,7 @@
|
|||
<td colspan="3">
|
||||
<label for="import_compat">Export Type:</label>
|
||||
<select id="import_compat" name="import_compat">
|
||||
<option value="yes">Import Compatible Export</option>
|
||||
<option value="yes">Import-Compatible Export</option>
|
||||
<option value="">Export all Data</option>
|
||||
</select>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import mock
|
|||
import unittest2
|
||||
|
||||
from ..controllers import main
|
||||
from ..common.session import OpenERPSession
|
||||
from ..session import OpenERPSession
|
||||
|
||||
class Placeholder(object):
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -6,7 +6,7 @@ import unittest2
|
|||
import simplejson
|
||||
|
||||
import web.controllers.main
|
||||
from ..common import nonliterals, session as s
|
||||
from .. import nonliterals, session as s
|
||||
|
||||
def field_attrs(fields_view_get, fieldname):
|
||||
(field,) = filter(lambda f: f['attrs'].get('name') == fieldname,
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 16137)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue