summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVishmita <vja@tinyerp.com>2012-10-11 15:58:37 +0530
committerVishmita <vja@tinyerp.com>2012-10-11 15:58:37 +0530
commitffe8dd604a833ae8c3f6b8d1fd6ccdfa02b256bb (patch)
tree44d7961b09e4678c0ba8f3c4bc1ece4469213b6c
parent3e15613cf7783df5175f52d0ba4945ba54890f98 (diff)
parent70afbd796e441cce26171af03c26a08f88c3ab52 (diff)
[MERGE]Merge Trunk
bzr revid: vja@tinyerp.com-20121011102837-k0y1kvf901rp3l8u
-rw-r--r--Makefile34
-rw-r--r--README.web5
-rw-r--r--addons/web/__init__.py33
-rw-r--r--addons/web/common/__init__.py6
-rw-r--r--addons/web/common/openerplib/__init__.py32
-rw-r--r--addons/web/common/openerplib/dates.py97
-rw-r--r--addons/web/common/openerplib/main.py311
-rw-r--r--addons/web/common/release.py10
-rw-r--r--addons/web/common/xml2json.py26
-rw-r--r--addons/web/controllers/main.py77
-rw-r--r--addons/web/doc/Makefile (renamed from doc/Makefile)0
-rw-r--r--addons/web/doc/_static/openerp.png (renamed from doc/_static/openerp.png)bin3981 -> 3981 bytes
-rw-r--r--addons/web/doc/_templates/sidebarintro.html (renamed from doc/_templates/sidebarintro.html)0
-rw-r--r--addons/web/doc/_templates/sidebarlogo.html (renamed from doc/_templates/sidebarlogo.html)0
-rw-r--r--addons/web/doc/_themes/LICENSE (renamed from doc/_themes/LICENSE)0
-rw-r--r--addons/web/doc/_themes/README (renamed from doc/_themes/README)0
-rw-r--r--addons/web/doc/_themes/flask/layout.html (renamed from doc/_themes/flask/layout.html)0
-rw-r--r--addons/web/doc/_themes/flask/relations.html (renamed from doc/_themes/flask/relations.html)0
-rw-r--r--addons/web/doc/_themes/flask/static/flasky.css_t (renamed from doc/_themes/flask/static/flasky.css_t)0
-rw-r--r--addons/web/doc/_themes/flask/static/small_flask.css (renamed from doc/_themes/flask/static/small_flask.css)0
-rw-r--r--addons/web/doc/_themes/flask/theme.conf (renamed from doc/_themes/flask/theme.conf)0
-rw-r--r--addons/web/doc/_themes/flask_small/layout.html (renamed from doc/_themes/flask_small/layout.html)0
-rw-r--r--addons/web/doc/_themes/flask_small/static/flasky.css_t (renamed from doc/_themes/flask_small/static/flasky.css_t)0
-rw-r--r--addons/web/doc/_themes/flask_small/theme.conf (renamed from doc/_themes/flask_small/theme.conf)0
-rw-r--r--addons/web/doc/_themes/flask_theme_support.py (renamed from doc/_themes/flask_theme_support.py)0
-rw-r--r--addons/web/doc/addon-structure.txt (renamed from doc/addon-structure.txt)0
-rw-r--r--addons/web/doc/async.rst (renamed from doc/async.rst)0
-rw-r--r--addons/web/doc/changelog-7.0.rst (renamed from doc/changelog-7.0.rst)10
-rw-r--r--addons/web/doc/conf.py (renamed from doc/conf.py)0
-rw-r--r--addons/web/doc/form-notes.rst (renamed from doc/form-notes.rst)0
-rw-r--r--addons/web/doc/guides/client-action.rst (renamed from doc/guides/client-action.rst)0
-rw-r--r--addons/web/doc/index.rst (renamed from doc/index.rst)0
-rw-r--r--addons/web/doc/list-view.rst (renamed from doc/list-view.rst)0
-rw-r--r--addons/web/doc/make.bat (renamed from doc/make.bat)0
-rw-r--r--addons/web/doc/rpc.rst (renamed from doc/rpc.rst)0
-rw-r--r--addons/web/doc/search-view.rst (renamed from doc/search-view.rst)0
-rw-r--r--addons/web/doc/widget.rst (renamed from doc/widget.rst)0
-rw-r--r--addons/web/http.py (renamed from addons/web/common/http.py)93
-rw-r--r--addons/web/nonliterals.py (renamed from addons/web/common/nonliterals.py)0
-rw-r--r--addons/web/session.py (renamed from addons/web/common/session.py)142
-rw-r--r--addons/web/static/src/css/base.css73
-rw-r--r--addons/web/static/src/css/base.sass47
-rw-r--r--addons/web/static/src/js/chrome.js30
-rw-r--r--addons/web/static/src/js/corelib.js13
-rw-r--r--addons/web/static/src/js/data.js8
-rw-r--r--addons/web/static/src/js/view_form.js185
-rw-r--r--addons/web/static/src/js/view_list_editable.js7
-rw-r--r--addons/web/static/src/js/views.js49
-rw-r--r--addons/web/static/src/xml/base.xml21
-rw-r--r--addons/web_calendar/static/src/js/calendar.js4
-rw-r--r--addons/web_diagram/controllers/main.py17
-rw-r--r--addons/web_diagram/static/src/js/diagram.js21
-rw-r--r--addons/web_gantt/static/src/js/gantt.js2
-rw-r--r--addons/web_graph/controllers/graph.py13
-rw-r--r--addons/web_kanban/static/src/js/kanban.js10
-rw-r--r--addons/web_tests/static/src/js/web_tests.js6
-rw-r--r--addons/web_view_editor/static/src/js/view_editor.js4
-rw-r--r--logging.json27
-rwxr-xr-xopenerp-web116
-rw-r--r--openerp-web.cfg3
-rwxr-xr-xsetup.py124
61 files changed, 457 insertions, 1199 deletions
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 4beb20d3eed..00000000000
--- a/Makefile
+++ /dev/null
@@ -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
-
diff --git a/README.web b/README.web
index 4c84cefdb22..7aff74c76c4 100644
--- a/README.web
+++ b/README.web
@@ -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
diff --git a/addons/web/__init__.py b/addons/web/__init__.py
index 621931e9167..3b447ce748d 100644
--- a/addons/web/__init__.py
+++ b/addons/web/__init__.py
@@ -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
diff --git a/addons/web/common/__init__.py b/addons/web/common/__init__.py
deleted file mode 100644
index 4060be2f87e..00000000000
--- a/addons/web/common/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/python
-from . import http
-from . import nonliterals
-from . import release
-from . import session
-from . import xml2json
diff --git a/addons/web/common/openerplib/__init__.py b/addons/web/common/openerplib/__init__.py
deleted file mode 100644
index 44701ec22fe..00000000000
--- a/addons/web/common/openerplib/__init__.py
+++ /dev/null
@@ -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 *
-
diff --git a/addons/web/common/openerplib/dates.py b/addons/web/common/openerplib/dates.py
deleted file mode 100644
index 6d4fa9e2f63..00000000000
--- a/addons/web/common/openerplib/dates.py
+++ /dev/null
@@ -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)
-
diff --git a/addons/web/common/openerplib/main.py b/addons/web/common/openerplib/main.py
deleted file mode 100644
index 93c2d1da69d..00000000000
--- a/addons/web/common/openerplib/main.py
+++ /dev/null
@@ -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)
-
diff --git a/addons/web/common/release.py b/addons/web/common/release.py
deleted file mode 100644
index b9dafd330d0..00000000000
--- a/addons/web/common/release.py
+++ /dev/null
@@ -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"
diff --git a/addons/web/common/xml2json.py b/addons/web/common/xml2json.py
deleted file mode 100644
index df3aad67fa5..00000000000
--- a/addons/web/common/xml2json.py
+++ /dev/null
@@ -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
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 85e9bc076be..72ffd9567f6 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -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 = {}
diff --git a/doc/Makefile b/addons/web/doc/Makefile
index c1eff186583..c1eff186583 100644
--- a/doc/Makefile
+++ b/addons/web/doc/Makefile
diff --git a/doc/_static/openerp.png b/addons/web/doc/_static/openerp.png
index d6dbd9d83d1..d6dbd9d83d1 100644
--- a/doc/_static/openerp.png
+++ b/addons/web/doc/_static/openerp.png
Binary files differ
diff --git a/doc/_templates/sidebarintro.html b/addons/web/doc/_templates/sidebarintro.html
index ecd51980e58..ecd51980e58 100644
--- a/doc/_templates/sidebarintro.html
+++ b/addons/web/doc/_templates/sidebarintro.html
diff --git a/doc/_templates/sidebarlogo.html b/addons/web/doc/_templates/sidebarlogo.html
index de6e3e5c851..de6e3e5c851 100644
--- a/doc/_templates/sidebarlogo.html
+++ b/addons/web/doc/_templates/sidebarlogo.html
diff --git a/doc/_themes/LICENSE b/addons/web/doc/_themes/LICENSE
index 8daab7ee6ef..8daab7ee6ef 100644
--- a/doc/_themes/LICENSE
+++ b/addons/web/doc/_themes/LICENSE
diff --git a/doc/_themes/README b/addons/web/doc/_themes/README
index b3292bdff8e..b3292bdff8e 100644
--- a/doc/_themes/README
+++ b/addons/web/doc/_themes/README
diff --git a/doc/_themes/flask/layout.html b/addons/web/doc/_themes/flask/layout.html
index ad08eccd38d..ad08eccd38d 100644
--- a/doc/_themes/flask/layout.html
+++ b/addons/web/doc/_themes/flask/layout.html
diff --git a/doc/_themes/flask/relations.html b/addons/web/doc/_themes/flask/relations.html
index 3bbcde85bb4..3bbcde85bb4 100644
--- a/doc/_themes/flask/relations.html
+++ b/addons/web/doc/_themes/flask/relations.html
diff --git a/doc/_themes/flask/static/flasky.css_t b/addons/web/doc/_themes/flask/static/flasky.css_t
index 0280e159455..0280e159455 100644
--- a/doc/_themes/flask/static/flasky.css_t
+++ b/addons/web/doc/_themes/flask/static/flasky.css_t
diff --git a/doc/_themes/flask/static/small_flask.css b/addons/web/doc/_themes/flask/static/small_flask.css
index 1c6df309edc..1c6df309edc 100644
--- a/doc/_themes/flask/static/small_flask.css
+++ b/addons/web/doc/_themes/flask/static/small_flask.css
diff --git a/doc/_themes/flask/theme.conf b/addons/web/doc/_themes/flask/theme.conf
index 18c720f804c..18c720f804c 100644
--- a/doc/_themes/flask/theme.conf
+++ b/addons/web/doc/_themes/flask/theme.conf
diff --git a/doc/_themes/flask_small/layout.html b/addons/web/doc/_themes/flask_small/layout.html
index aa1716aaff0..aa1716aaff0 100644
--- a/doc/_themes/flask_small/layout.html
+++ b/addons/web/doc/_themes/flask_small/layout.html
diff --git a/doc/_themes/flask_small/static/flasky.css_t b/addons/web/doc/_themes/flask_small/static/flasky.css_t
index fe2141c565e..fe2141c565e 100644
--- a/doc/_themes/flask_small/static/flasky.css_t
+++ b/addons/web/doc/_themes/flask_small/static/flasky.css_t
diff --git a/doc/_themes/flask_small/theme.conf b/addons/web/doc/_themes/flask_small/theme.conf
index 542b462515d..542b462515d 100644
--- a/doc/_themes/flask_small/theme.conf
+++ b/addons/web/doc/_themes/flask_small/theme.conf
diff --git a/doc/_themes/flask_theme_support.py b/addons/web/doc/_themes/flask_theme_support.py
index 33f47449c11..33f47449c11 100644
--- a/doc/_themes/flask_theme_support.py
+++ b/addons/web/doc/_themes/flask_theme_support.py
diff --git a/doc/addon-structure.txt b/addons/web/doc/addon-structure.txt
index 38151749651..38151749651 100644
--- a/doc/addon-structure.txt
+++ b/addons/web/doc/addon-structure.txt
diff --git a/doc/async.rst b/addons/web/doc/async.rst
index 23b3409bd8f..23b3409bd8f 100644
--- a/doc/async.rst
+++ b/addons/web/doc/async.rst
diff --git a/doc/changelog-7.0.rst b/addons/web/doc/changelog-7.0.rst
index 00d0236f214..dd08f1bcde7 100644
--- a/doc/changelog-7.0.rst
+++ b/addons/web/doc/changelog-7.0.rst
@@ -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
----------------
diff --git a/doc/conf.py b/addons/web/doc/conf.py
index 83fc9693f02..83fc9693f02 100644
--- a/doc/conf.py
+++ b/addons/web/doc/conf.py
diff --git a/doc/form-notes.rst b/addons/web/doc/form-notes.rst
index 98d4a4de67e..98d4a4de67e 100644
--- a/doc/form-notes.rst
+++ b/addons/web/doc/form-notes.rst
diff --git a/doc/guides/client-action.rst b/addons/web/doc/guides/client-action.rst
index 30f0c7eeb27..30f0c7eeb27 100644
--- a/doc/guides/client-action.rst
+++ b/addons/web/doc/guides/client-action.rst
diff --git a/doc/index.rst b/addons/web/doc/index.rst
index 6a0d8dee2d4..6a0d8dee2d4 100644
--- a/doc/index.rst
+++ b/addons/web/doc/index.rst
diff --git a/doc/list-view.rst b/addons/web/doc/list-view.rst
index b7cdc1cd039..b7cdc1cd039 100644
--- a/doc/list-view.rst
+++ b/addons/web/doc/list-view.rst
diff --git a/doc/make.bat b/addons/web/doc/make.bat
index 3e72cadb53b..3e72cadb53b 100644
--- a/doc/make.bat
+++ b/addons/web/doc/make.bat
diff --git a/doc/rpc.rst b/addons/web/doc/rpc.rst
index 4787978e187..4787978e187 100644
--- a/doc/rpc.rst
+++ b/addons/web/doc/rpc.rst
diff --git a/doc/search-view.rst b/addons/web/doc/search-view.rst
index 16a91c5e9ac..16a91c5e9ac 100644
--- a/doc/search-view.rst
+++ b/addons/web/doc/search-view.rst
diff --git a/doc/widget.rst b/addons/web/doc/widget.rst
index ddfe525227d..ddfe525227d 100644
--- a/doc/widget.rst
+++ b/addons/web/doc/widget.rst
diff --git a/addons/web/common/http.py b/addons/web/http.py
index 4955876894d..65630768737 100644
--- a/addons/web/common/http.py
+++ b/addons/web/http.py
@@ -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)
-
- if options.session_storage:
- if not os.path.exists(options.session_storage):
- os.mkdir(options.session_storage, 0700)
- self.session_storage = options.session_storage
+ app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
+ self.dispatch = DisableCacheMiddleware(app)
+
+ 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:
diff --git a/addons/web/common/nonliterals.py b/addons/web/nonliterals.py
index 4ee6d122f44..4ee6d122f44 100644
--- a/addons/web/common/nonliterals.py
+++ b/addons/web/nonliterals.py
diff --git a/addons/web/common/session.py b/addons/web/session.py
index 45bbb32f64c..e57d3da5b45 100644
--- a/addons/web/common/session.py
+++ b/addons/web/session.py
@@ -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
+# OpenERPSession RPC openerp backend access
#----------------------------------------------------------
-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 AuthenticationError(Exception):
+ pass
-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)
+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
-#----------------------------------------------------------
-# OpenERPSession RPC openerp backend access
-#----------------------------------------------------------
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
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index 3d54aef0fa2..c8f1567bee9 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -25,7 +25,6 @@
display: none !important;
}
}
-
.openerp.openerp_webclient_container {
height: 100%;
position: relative;
@@ -46,7 +45,7 @@
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
/* http://www.quirksmode.org/dom/inputfile.html
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
- */ */
+ */
}
.openerp :-moz-placeholder {
color: #afafb6 !important;
@@ -69,10 +68,13 @@
background-color: #f0f0f0;
}
.openerp thead th {
- border-right: 1px dotted #afafb6;
+ border-left: 1px solid #dfdfdf;
}
-.openerp thead th:last-child {
- border-right: none;
+.openerp thead th:first-child {
+ border-left: none;
+}
+.openerp thead th.null {
+ border-left: none;
}
.openerp th, .openerp td {
padding: 0;
@@ -522,28 +524,6 @@
.openerp .oe_grey {
color: #aaaaaa;
}
-.openerp header {
- position: relative;
- border-bottom: 1px solid #cacaca;
- padding-left: 2px;
- background-color: #fcfcfc;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
- background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
- background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
-}
-.openerp header > span {
- margin-left: 4px;
-}
-.openerp header ul {
- display: inline-block;
- float: right;
-}
-.openerp header .oe_button {
- margin: 3px 2px 1px;
-}
.openerp .oe_tag {
border: 1px solid #afafb6;
font-size: 11px;
@@ -1204,7 +1184,7 @@
color: white;
padding: 2px 4px;
margin: 1px 6px 0 0;
- border: 1px solid lightGray;
+ border: 1px solid lightgrey;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
@@ -1229,7 +1209,7 @@
transform: scale(1.1);
}
.openerp .oe_secondary_submenu .oe_active {
- border-top: 1px solid lightGray;
+ border-top: 1px solid lightgrey;
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);
@@ -2044,6 +2024,31 @@
.openerp .oe_application .oe_form_sheet .oe_notebook_page {
padding: 0 16px;
}
+.openerp .oe_form header {
+ position: relative;
+ border-bottom: 1px solid #cacaca;
+ padding-left: 2px;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+}
+.openerp .oe_form header > span {
+ margin-left: 4px;
+}
+.openerp .oe_form header ul {
+ display: inline-block;
+ float: right;
+}
+.openerp .oe_form header .oe_button {
+ margin: 3px 2px 1px;
+}
+.openerp .oe_form header .oe_button:first-child {
+ margin-left: 6px;
+}
.openerp .oe_form header .oe_tags {
margin: 5px 0 0 5px;
width: 400px;
@@ -2125,7 +2130,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;
@@ -2294,16 +2299,16 @@
margin-bottom: 32px;
text-align: justify;
}
-.openerp .oe_form_editable .oe_form .oe_form_field_integer {
+.openerp .oe_form_editable .oe_form .oe_form_field_integer input {
width: 6em !important;
}
-.openerp .oe_form_editable .oe_form .oe_form_field_float {
+.openerp .oe_form_editable .oe_form .oe_form_field_float input {
width: 7em !important;
}
-.openerp .oe_form_editable .oe_form .oe_form_field_date {
+.openerp .oe_form_editable .oe_form .oe_form_field_date input {
width: 7.5em !important;
}
-.openerp .oe_form_editable .oe_form .oe_form_field_datetime {
+.openerp .oe_form_editable .oe_form .oe_form_field_datetime input {
width: 11.5em !important;
}
.openerp .oe_hidden_input_file {
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 0567d31b1eb..823ced4e24f 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -180,9 +180,11 @@ $sheet-max-width: 860px
font-weight: bold
background-color: #f0f0f0
th
- border-right: 1px dotted $tag-border
- &:last-child
- border-right: none
+ border-left: 1px solid #dfdfdf
+ &:first-child
+ border-left: none
+ &.null
+ border-left: none
th, td
padding: 0
text-align: left
@@ -457,21 +459,6 @@ $sheet-max-width: 860px
// }}}
- // Generic blocks {{{
- header
- position: relative
- border-bottom: 1px solid #cacaca
- padding-left: 2px
- @include vertical-gradient(#fcfcfc, #dedede)
- > span
- margin-left: 4px
- ul
- display: inline-block
- float: right
- .oe_button
- margin: 3px 2px 1px
- // }}}
-
// Tags (for many2many tags, among others) {{{
.oe_tag
border: 1px solid $tag-border
@@ -1618,6 +1605,22 @@ $sheet-max-width: 860px
.oe_notebook_page
padding: 0 16px
// }}}
+ // FormView.header {{{
+ .oe_form header
+ position: relative
+ border-bottom: 1px solid #cacaca
+ padding-left: 2px
+ @include vertical-gradient(#fcfcfc, #dedede)
+ > span
+ margin-left: 4px
+ ul
+ display: inline-block
+ float: right
+ .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
@@ -1822,13 +1825,13 @@ $sheet-max-width: 860px
.oe_form_editable
.oe_form
- .oe_form_field_integer
+ .oe_form_field_integer input
width: 6em !important
- .oe_form_field_float
+ .oe_form_field_float input
width: 7em !important
- .oe_form_field_date
+ .oe_form_field_date input
width: 7.5em !important
- .oe_form_field_datetime
+ .oe_form_field_datetime input
width: 11.5em !important
// }}}
// FormView.fields_binary {{{
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 80693d26e46..b605f4ad4e1 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -181,7 +181,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 +190,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 +203,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 +219,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 +236,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);
@@ -957,7 +957,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 +1007,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,15 +1016,13 @@ instance.web.WebClient = instance.web.Client.extend({
},
show_login: function() {
this.toggle_bars(false);
-
+
+ var state = $.bbq.getState(true);
var action = {
'type': 'ir.actions.client',
- 'tag': 'login'
+ 'tag': 'login',
+ 'params': state
};
- var state = $.bbq.getState(true);
- if (state.action === "login") {
- action.params = state;
- }
this.action_manager.do_action(action);
this.action_manager.inner_widget.on('login_successful', this, function() {
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index ff193605033..b5fbacc7927 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -121,6 +121,10 @@ openerp.web.corelib = function(instance) {
// The dummy class constructor
function Class() {
+ if(this.constructor !== instance.web.Class){
+ throw new Error("You can only instanciate objects with the 'new' operator");
+ return null;
+ }
// All construction is actually done in the init method
if (!initializing && this.init) {
var ret = this.init.apply(this, arguments);
@@ -976,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
@@ -1332,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,
@@ -1344,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);
}
});
});
@@ -1430,8 +1435,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
return deferred;
}
},
- on_rpc_error: function(error) {
- },
get_url: function (file) {
return this.prefix + file;
},
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index 220d42737da..ce85e0ac43f 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -575,6 +575,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @param {Number|String} ids identifier of the record to delete
*/
unlink: function(ids) {
+ this.trigger('unlink', ids);
return this._model.call('unlink', [ids], {context: this._model.context()});
},
/**
@@ -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({
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index a17d43a0b5b..afb5e258c0b 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -126,6 +126,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.on("change:actual_mode", self, self.init_pager);
self.init_pager();
});
+ self.on("load_record", self, self.load_record);
instance.web.bus.on('clear_uncommitted_changes', this, function(e) {
if (!this.can_be_discarded()) {
e.preventDefault();
@@ -172,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) {
@@ -331,7 +332,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
fields.push('display_name');
return self.dataset.read_index(fields, {
context: { 'bin_size': true, 'future_display_name' : true }
- }).pipe(self.on_record_loaded);
+ }).pipe(function(r) {
+ self.trigger('load_record', r);
+ });
});
}
return shown.pipe(function() {
@@ -354,7 +357,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
this._super();
},
- on_record_loaded: function(record) {
+ load_record: function(record) {
var self = this, set_values = [];
if (!record) {
this.set({ 'title' : undefined });
@@ -413,12 +416,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
* @return {$.Deferred}
*/
load_defaults: function () {
+ var self = this;
var keys = _.keys(this.fields_view.fields);
if (keys.length) {
- return this.dataset.default_get(keys)
- .pipe(this.on_record_loaded);
+ return this.dataset.default_get(keys).pipe(function(r) {
+ self.trigger('load_record', r);
+ });
}
- return this.on_record_loaded({});
+ return self.trigger('load_record', {});
},
on_form_changed: function() {
this.trigger("view_content_has_changed");
@@ -610,7 +615,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();
}
});
@@ -645,7 +650,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();
}
},
@@ -752,9 +757,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.trigger('history_back');
} else {
this.to_view_mode();
- this.on_record_loaded(this.datarecord);
+ this.trigger('load_record', this.datarecord);
}
}
+ this.trigger('on_button_cancel');
return false;
},
on_button_new: function() {
@@ -778,7 +784,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({ result : new_id });
}).then(function() {
return self.to_edit_mode();
}).then(function() {
@@ -855,7 +861,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
@@ -864,7 +870,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;
@@ -891,12 +897,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;
});
}
@@ -914,9 +923,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;
@@ -930,9 +941,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});
});
}
@@ -952,9 +964,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
} else {
var fields = _.keys(self.fields_view.fields);
fields.push('display_name');
- return self.dataset.read_index(fields, {
- context : { 'bin_size' : true, 'future_display_name' : true }
- }).pipe(self.on_record_loaded);
+ return self.dataset.read_index(fields,
+ {
+ context: {
+ 'bin_size': true,
+ 'future_display_name': true
+ }
+ }).pipe(function(r) {
+ self.trigger('load_record', r);
+ });
}
});
},
@@ -1618,13 +1636,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) {
@@ -2068,7 +2087,11 @@ 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;
- this.options = JSON.parse(this.node.attrs.options || '{}');
+ 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.set({'value': false});
this.on("change:value", this, function() {
@@ -2165,11 +2188,12 @@ instance.web.form.ReinitializeWidgetMixin = {
this.initialize_field();
},
initialize_field: function() {
- this.on("change:effective_readonly", this, function() {
- this.destroy_content();
- this.renderElement();
- this.initialize_content();
- });
+ this.on("change:effective_readonly", this, this.reinitialize);
+ this.initialize_content();
+ },
+ reinitialize: function() {
+ this.destroy_content();
+ this.renderElement();
this.initialize_content();
},
/**
@@ -2190,9 +2214,10 @@ instance.web.form.ReinitializeWidgetMixin = {
instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, {
initialize_field: function() {
instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this);
- this.on("change:effective_readonly", this, function() {
- this.render_value();
- });
+ this.render_value();
+ },
+ reinitialize: function() {
+ instance.web.form.ReinitializeWidgetMixin.reinitialize.call(this);
this.render_value();
},
/**
@@ -2212,7 +2237,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
var self = this;
var $input = this.$el.find('input');
$input.change(function() {
- self.set({'value': instance.web.parse_value($input.val(), self)});
+ self.set({'value': self.parse_value($input.val())});
});
this.setupFocus($input);
},
@@ -2221,20 +2246,20 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
this.render_value();
},
render_value: function() {
- var show_value = instance.web.format_value(this.get('value'), this, '');
+ var show_value = this.format_value(this.get('value'), '');
if (!this.get("effective_readonly")) {
this.$el.find('input').val(show_value);
} else {
if (this.password) {
show_value = new Array(show_value.length + 1).join('*');
}
- this.$el.text(show_value);
+ this.$(".oe_form_char_content").text(show_value);
}
},
is_syntax_valid: function() {
if (!this.get("effective_readonly")) {
try {
- var value_ = instance.web.parse_value(this.$el.find('input').val(), this, '');
+ var value_ = this.parse_value(this.$el.find('input').val(), '');
return true;
} catch(e) {
return false;
@@ -2242,6 +2267,12 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
}
return true;
},
+ parse_value: function(val, def) {
+ return instance.web.parse_value(val, this, def);
+ },
+ format_value: function(val, def) {
+ return instance.web.format_value(val, this, def);
+ },
is_false: function() {
return this.get('value') === '' || this._super();
},
@@ -2959,7 +2990,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
title: _t("Open: ") + self.string
}
);
- pop.on('on_write_complete', self, function(){
+ pop.on('write_completed', self, function(){
self.display_value = {};
self.render_value();
self.focus();
@@ -3285,11 +3316,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,
@@ -3357,9 +3384,9 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
if (self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$el).children().remove();
}
- controller.on_record_loaded.add_last(function() {
- once.resolve();
- });
+ controller.on("load_record", self, function(){
+ once.resolve();
+ });
controller.on_pager_action.add_first(function() {
self.save_any_view();
});
@@ -3368,10 +3395,13 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}
def.resolve();
});
- this.viewmanager.on_mode_switch.add_first(function(n_mode, b, c, d, e) {
+ this.viewmanager.on("switch_mode", self, function(n_mode, b, c, d, e) {
$.when(self.save_any_view()).then(function() {
- if(n_mode === "list")
- $.async_when().then(function() {self.reload_current_view();});
+ if (n_mode === "list") {
+ $.async_when().then(function() {
+ self.reload_current_view();
+ });
+ }
});
});
this.is_setted.then(function() {
@@ -3539,7 +3569,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
});
this.__ignore_blur = false;
},
- switch_view: function(mode, unused) {
+ switch_mode: function(mode, unused) {
if (mode !== 'form') {
return this._super(mode, unused);
}
@@ -3793,7 +3823,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()) {
@@ -3996,7 +4026,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();
});
@@ -4108,7 +4138,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
title: _t("Open: ") + this.m2m_field.string,
readonly: this.getParent().get("effective_readonly")
});
- pop.on('on_write_complete', self, self.reload_content);
+ pop.on('write_completed', self, self.reload_content);
}
});
@@ -4129,7 +4159,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();
});
@@ -4177,7 +4207,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);
});
@@ -4356,7 +4386,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
this.dataset.write_function = function(id, data, options, sup) {
var fct = self.options.write_function || sup;
return fct.call(this, id, data, options).then(function() {
- self.trigger('on_write_complete');
+ self.trigger('write_completed');
});
};
this.dataset.parent_view = this.options.parent_view;
@@ -4790,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;
});
@@ -4989,6 +5019,44 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
},
});
+instance.web.form.FieldMonetary = instance.web.form.FieldFloat.extend({
+ template: "FieldMonetary",
+ init: function() {
+ this._super.apply(this, arguments);
+ this.set({"currency": false});
+ if (this.options.currency_field) {
+ this.field_manager.on("field_changed:" + this.options.currency_field, this, function() {
+ this.set({"currency": this.field_manager.get_field_value(this.options.currency_field)});
+ });
+ }
+ this.on("change:currency", this, this.get_currency_info);
+ this.get_currency_info();
+ this.ci_dm = new instance.web.DropMisordered();
+ },
+ start: function() {
+ var tmp = this._super();
+ this.on("change:currency_info", this, this.reinitialize);
+ return tmp;
+ },
+ get_currency_info: function() {
+ var self = this;
+ if (this.get("currency") === false) {
+ this.set({"currency_info": null});
+ return;
+ }
+ return this.ci_dm.add(new instance.web.Model("res.currency").query(["symbol", "position"])
+ .filter([["id", "=", self.get("currency")]]).first()).pipe(function(res) {
+ self.set({"currency_info": res});
+ });
+ },
+ parse_value: function(val, def) {
+ return instance.web.parse_value(val, {type: "float"}, def);
+ },
+ format_value: function(val, def) {
+ return instance.web.format_value(val, {type: "float"}, def);
+ },
+});
+
/**
* Registry of form fields, called by :js:`instance.web.FormView`.
*
@@ -5019,7 +5087,8 @@ instance.web.form.widgets = new instance.web.Registry({
'progressbar': 'instance.web.form.FieldProgressBar',
'image': 'instance.web.form.FieldBinaryImage',
'binary': 'instance.web.form.FieldBinaryFile',
- 'statusbar': 'instance.web.form.FieldStatus'
+ 'statusbar': 'instance.web.form.FieldStatus',
+ 'monetary': 'instance.web.form.FieldMonetary',
});
/**
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 892221a347b..fe0f525fe3f 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -626,7 +626,7 @@ openerp.web.list_editable = function (instance) {
},
start: function () {
var self = this;
- var _super = this._super();
+ var _super = this._super();
this.form.embedded_view = this._validate_view(
this.delegate.edition_view(this));
var form_ready = this.form.appendTo(this.$el).then(
@@ -708,10 +708,9 @@ openerp.web.list_editable = function (instance) {
var self = this;
var form = self.form;
var loaded = record
- ? form.on_record_loaded(_.extend({}, record))
+ ? form.trigger('load_record', _.extend({}, record))
: form.load_defaults();
-
- return loaded.pipe(function () {
+ return $.when(loaded).pipe(function () {
return form.do_show({reload: false});
}).pipe(function () {
self.record = form.datarecord;
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index b98d6d4802b..f985e5f6dc8 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -417,7 +417,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
this._super();
var self = this;
this.$el.find('.oe_view_manager_switch a').click(function() {
- self.on_mode_switch($(this).data('view-type'));
+ self.switch_mode($(this).data('view-type'));
}).tipsy();
var views_ids = {};
_.each(this.views_src, function(view) {
@@ -439,24 +439,17 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
- return this.on_mode_switch(default_view);
+ return this.switch_mode(default_view);
},
- /**
- * Asks the view manager to switch visualization mode.
- *
- * @param {String} view_type type of view to display
- * @param {Boolean} [no_store=false] don't store the view being switched to on the switch stack
- * @returns {jQuery.Deferred} new view loading promise
- */
- on_mode_switch: function(view_type, no_store, view_options) {
+ switch_mode: function(view_type, no_store, view_options) {
var self = this;
var view = this.views[view_type];
var view_promise;
var form = this.views['form'];
if (!view || (form && form.controller && !form.controller.can_be_discarded())) {
+ self.trigger('switch_mode', view_type, no_store, view_options);
return $.Deferred().reject();
}
-
if (!no_store) {
this.views_history.push(view_type);
}
@@ -474,13 +467,12 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.searchview[(view.controller.searchable === false || this.searchview.hidden) ? 'hide' : 'show']();
}
- this.$el
- .find('.oe_view_manager_switch a').parent().removeClass('active');
+ this.$el.find('.oe_view_manager_switch a').parent().removeClass('active');
this.$el
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
.parent().addClass('active');
- return $.when(view_promise).then(function () {
+ r = $.when(view_promise).then(function () {
_.each(_.keys(self.views), function(view_name) {
var controller = self.views[view_name].controller;
if (controller) {
@@ -501,7 +493,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
}
});
+ self.trigger('switch_mode', view_type, no_store, view_options);
});
+ return r;
},
do_create_view: function(view_type) {
// Lazy loading of views
@@ -530,7 +524,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
- controller.do_switch_view.add_last(_.bind(this.switch_view, this));
+ controller.on('switch_mode', self, this.switch_mode);
controller.do_prev_view.add_last(this.on_prev_view);
var container = this.$el.find(".oe_view_manager_view_" + view_type);
@@ -552,7 +546,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
add_breadcrumb: function(on_reverse_breadcrumb) {
var self = this;
var views = [this.active_view || this.views_src[0].view_type];
- this.on_mode_switch.add(function(mode) {
+ this.on('switch_mode', self, function(mode) {
var last = views.slice(-1)[0];
if (mode !== last) {
if (mode !== 'form') {
@@ -568,7 +562,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
var view_to_select = views[index];
self.$el.show();
if (self.active_view !== view_to_select) {
- self.on_mode_switch(view_to_select);
+ self.switch_mode(view_to_select);
}
},
get_title: function() {
@@ -599,17 +593,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
});
},
/**
- * Method used internally when a view asks to switch view. This method is meant
- * to be extended by child classes to change the default behavior, which simply
- * consist to switch to the asked view.
- */
- switch_view: function(view_type, no_store, options) {
- return this.on_mode_switch(view_type, no_store, options);
- },
- /**
* Returns to the view preceding the caller view in this manager's
* navigation history (the navigation history is appended to via
- * on_mode_switch)
+ * switch_mode)
*
* @param {Object} [options]
* @param {Boolean} [options.created=false] resource was created
@@ -623,12 +609,12 @@ instance.web.ViewManager = instance.web.Widget.extend({
if (options.created && current_view === 'form' && previous_view === 'list') {
// APR special case: "If creation mode from list (and only from a list),
// after saving, go to page view (don't come back in list)"
- return this.on_mode_switch('form');
+ return this.switch_mode('form');
} else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
// APR special case: "If creation from dashboard, we have no previous view
- return this.on_mode_switch('form');
+ return this.switch_mode('form');
}
- return this.on_mode_switch(previous_view, true);
+ return this.switch_mode(previous_view, true);
},
/**
* Sets up the current viewmanager's search view.
@@ -897,7 +883,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
}, action || {});
this.do_action(action);
},
- on_mode_switch: function (view_type, no_store, options) {
+ switch_mode: function (view_type, no_store, options) {
var self = this;
return $.when(this._super.apply(this, arguments)).then(function () {
@@ -932,7 +918,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
if (state.view_type && state.view_type !== this.active_view) {
defs.push(
this.views[this.active_view].deferred.pipe(function() {
- return self.on_mode_switch(state.view_type, true);
+ return self.switch_mode(state.view_type, true);
})
);
}
@@ -1271,6 +1257,7 @@ instance.web.View = instance.web.Widget.extend({
* @param {String} view view type to switch to
*/
do_switch_view: function(view) {
+ this.trigger('switch_mode',view);
},
/**
* Cancels the switch to the current view, switches to the previous one
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index bf55db8d7c2..a09897862f6 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -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">
@@ -916,7 +916,7 @@
</t>
<t t-name="FieldChar">
<span t-att-class="'oe_form_field '+widget.widget_class" t-att-style="widget.node.attrs.style">
- <t t-if="!widget.get('effective_readonly')">
+ <t t-if="!widget.get('effective_readonly')">
<input t-att-type="widget.password ? 'password' : 'text'"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
@@ -925,6 +925,9 @@
t-att-maxlength="widget.field.size"
/><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
</t>
+ <t t-if="widget.get('effective_readonly')">
+ <span class="oe_form_char_content"></span>
+ </t>
</span>
</t>
<t t-name="FieldEmail">
@@ -1534,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>
@@ -1717,4 +1720,16 @@
<button class="oe_form_m2o_sc_button oe_button">Add All Info...</button>
<button class="oe_form_m2o_cancel_button oe_button">Cancel</button>
</t>
+<t t-name="FieldMonetary" t-extend="FieldChar">
+ <t t-jquery="t:first" t-operation="before">
+ <t t-if="widget.get('currency_info') and widget.get('currency_info').position === 'before'">
+ <t t-esc="widget.get('currency_info').symbol"/>
+ </t>
+ </t>
+ <t t-jquery="t:last" t-operation="after">
+ <t t-if="widget.get('currency_info') and widget.get('currency_info').position === 'after'">
+ <t t-esc="widget.get('currency_info').symbol"/>
+ </t>
+ </t>
+</t>
</templates>
diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js
index 81533413312..d08912bbaab 100644
--- a/addons/web_calendar/static/src/js/calendar.js
+++ b/addons/web_calendar/static/src/js/calendar.js
@@ -458,8 +458,8 @@ instance.web_calendar.CalendarFormDialog = instance.web.Dialog.extend({
pager: false
});
var def = 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', self, this.on_form_dialog_saved);
this.form.on_button_cancel = function() {
self.close();
}
diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py
index e3d626ad48b..6af1f91adc0 100644
--- a/addons/web_diagram/controllers/main.py
+++ b/addons/web_diagram/controllers/main.py
@@ -1,21 +1,14 @@
-try:
- # embedded
- import openerp.addons.web.common.http as openerpweb
- from openerp.addons.web.controllers.main import View
-except ImportError:
- # standalone
- import web.common.http as openerpweb
- from web.controllers.main import View
-
-class DiagramView(View):
+import openerp
+
+class DiagramView(openerp.addons.web.controllers.main.View):
_cp_path = "/web_diagram/diagram"
- @openerpweb.jsonrequest
+ @openerp.addons.web.http.jsonrequest
def load(self, req, model, view_id):
fields_view = self.fields_view_get(req, model, view_id, 'diagram')
return {'fields_view': fields_view}
- @openerpweb.jsonrequest
+ @openerp.addons.web.http.jsonrequest
def get_diagram_info(self, req, id, model, node, connector,
src_node, des_node, label, **kw):
diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js
index 508aeb30563..9fd74853c3c 100644
--- a/addons/web_diagram/static/src/js/diagram.js
+++ b/addons/web_diagram/static/src/js/diagram.js
@@ -239,23 +239,23 @@ instance.web.DiagramView = instance.web.View.extend({
title: _t("Open: ") + title
}
);
-
- pop.on_write.add(function() {
+ pop.on('on_write_complete', self, function() {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
-
+
var form_fields = [self.parent_field];
var form_controller = pop.view_form;
- form_controller.on_record_loaded.add_first(function() {
+ form_controller.on("load_record", self, function(){
_.each(form_fields, function(fld) {
if (!(fld in form_controller.fields)) { return; }
var field = form_controller.fields[fld];
field.$input.prop('disabled', true);
field.$drop_down.unbind();
- field.$menu_btn.unbind();
});
- });
+ });
+
+
},
// Creates a popup to add a node to the diagram
@@ -280,7 +280,7 @@ instance.web.DiagramView = instance.web.View.extend({
var form_controller = pop.view_form;
var form_fields = [this.parent_field];
- form_controller.on_record_loaded.add_last(function() {
+ form_controller.on("load_record", self, function(){
_.each(form_fields, function(fld) {
if (!(fld in form_controller.fields)) { return; }
var field = form_controller.fields[fld];
@@ -303,7 +303,7 @@ instance.web.DiagramView = instance.web.View.extend({
title: _t("Open: ") + title
}
);
- pop.on_write.add(function() {
+ pop.on('on_write_complete', self, function() {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
},
@@ -339,12 +339,13 @@ instance.web.DiagramView = instance.web.View.extend({
var form_controller = pop.view_form;
- form_controller.on_record_loaded.add_last(function () {
+
+ form_controller.on("load_record", self, function(){
form_controller.fields[self.connectors.attrs.source].set_value(node_source_id);
form_controller.fields[self.connectors.attrs.source].dirty = true;
form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id);
form_controller.fields[self.connectors.attrs.destination].dirty = true;
- });
+ });
},
on_pager_action: function(action) {
diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js
index 0d600712d4a..66d699c2cde 100644
--- a/addons/web_gantt/static/src/js/gantt.js
+++ b/addons/web_gantt/static/src/js/gantt.js
@@ -211,7 +211,7 @@ instance.web_gantt.GanttView = instance.web.View.extend({
on_task_display: function(task) {
var self = this;
var pop = new instance.web.form.FormOpenPopup(self);
- pop.on('on_write_complete',self,self.reload);
+ pop.on('write_completed',self,self.reload);
pop.show_element(
self.dataset.model,
task.id,
diff --git a/addons/web_graph/controllers/graph.py b/addons/web_graph/controllers/graph.py
index e5c15898155..21ec7d6e973 100644
--- a/addons/web_graph/controllers/graph.py
+++ b/addons/web_graph/controllers/graph.py
@@ -1,19 +1,12 @@
# -*- coding: utf-8 -*-
-try:
- # embedded
- import openerp.addons.web.common.http as openerpweb
- from openerp.addons.web.controllers.main import View
-except ImportError:
- # standalone
- import web.common.http as openerpweb
- from web.controllers.main import View
+import openerp
from lxml import etree
-class GraphView(View):
+class GraphView(openerp.addons.web.controllers.main.View):
_cp_path = '/web_graph/graph'
- @openerpweb.jsonrequest
+ @openerp.addons.web.http.jsonrequest
def data_get(self, req, model=None, domain=[], context={}, group_by=[], view_id=False, orientation=False, stacked=False, mode="bar", **kwargs):
obj = req.session.model(model)
diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js
index 41cc366226f..537325394e9 100644
--- a/addons/web_kanban/static/src/js/kanban.js
+++ b/addons/web_kanban/static/src/js/kanban.js
@@ -194,9 +194,9 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
});
var am = instance.webclient.action_manager;
var form = am.dialog_widget.views.form.controller;
- form.on_button_cancel.add_last(am.dialog.on_close);
- form.on_created.add_last(function(r) {
- (new instance.web.DataSet(self, self.group_by_field.relation)).name_get([r.result]).then(function(new_record) {
+ form.on("on_button_cancel", self, am.dialog.on_close);
+ form.on('record_created', self, function(r) {
+ (new instance.web.DataSet(self, self.group_by_field.relation)).name_get([r]).then(function(new_record) {
am.dialog.on_close();
var domain = self.dataset.domain.slice(0);
domain.push([self.group_by, '=', new_record[0][0]]);
@@ -672,8 +672,8 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({
});
var am = instance.webclient.action_manager;
var form = am.dialog_widget.views.form.controller;
- form.on_button_cancel.add_last(am.dialog.on_close);
- form.on_saved.add_last(function() {
+ form.on("on_button_cancel", self, am.dialog.on_close);
+ form.on('record_saved', self, function() {
am.dialog.on_close();
self.view.do_reload();
});
diff --git a/addons/web_tests/static/src/js/web_tests.js b/addons/web_tests/static/src/js/web_tests.js
index 5b9c1d02503..bc7d16913fb 100644
--- a/addons/web_tests/static/src/js/web_tests.js
+++ b/addons/web_tests/static/src/js/web_tests.js
@@ -23,14 +23,14 @@ openerp.web_tests = function (instance) {
on_everything_loaded: function (slice) {
var records = slice[0].records;
if (!records.length) {
- this.form.on_record_loaded({});
+ this.form.trigger("load_record", {});
return;
}
- this.form.on_record_loaded(records[0]);
+ this.form.trigger("load_record", records[0]);
_(records.slice(1)).each(function (record, index) {
this.dataset.index = index+1;
this.form.reposition($('<div>').appendTo(this.$el));
- this.form.on_record_loaded(record);
+ this.form.trigger("load_record", record);
}, this);
}
});
diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js
index d9586eb415d..bde683ec2f2 100644
--- a/addons/web_view_editor/static/src/js/view_editor.js
+++ b/addons/web_view_editor/static/src/js/view_editor.js
@@ -1014,8 +1014,8 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
var action_manager = new instance.web.ActionManager(self);
$.when(action_manager.do_action(action)).then(function() {
var controller = action_manager.dialog_widget.views['form'].controller;
- controller.on_button_cancel.add_last(function(){
- action_manager.destroy()
+ controller.on("on_button_cancel", self, function(){
+ action_manager.destroy();
});
controller.do_save.add_last(function(){
action_manager.destroy();
diff --git a/logging.json b/logging.json
deleted file mode 100644
index 1f982a6d4d0..00000000000
--- a/logging.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "version": 1,
- "formatters": {
- "simple": {
- "format": "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
- }
- },
- "handlers": {
- "console": {
- "class": "logging.StreamHandler",
- "level": "DEBUG",
- "formatter": "simple",
- "stream": "ext://sys.stdout"
- }
- },
- "loggers": {
- "web": {
- },
- "web.common.openerplib": {
- "level": "INFO"
- }
- },
- "root": {
- "level": "DEBUG",
- "handlers": ["console"]
- }
-}
diff --git a/openerp-web b/openerp-web
deleted file mode 100755
index 7e536a99442..00000000000
--- a/openerp-web
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python
-import json
-import logging
-import logging.config
-import optparse
-import os
-import sys
-import tempfile
-
-import werkzeug.serving
-import werkzeug.contrib.fixers
-
-optparser = optparse.OptionParser()
-optparser.add_option("-s", "--session-path", dest="session_storage",
- default=os.path.join(tempfile.gettempdir(), "oe-sessions"),
- help="Directory used for session storage", metavar="DIR")
-optparser.add_option("--server-host", dest="server_host",
- default='127.0.0.1', help="OpenERP server hostname", metavar="HOST")
-optparser.add_option("--server-port", dest="server_port", default=8069,
- help="OpenERP server port", type="int", metavar="NUMBER")
-optparser.add_option("--db-filter", dest="dbfilter", default='.*',
- help="Filter listed databases", metavar="REGEXP")
-optparser.add_option('--addons-path', dest='addons_path', default=[], action='append',
- help="Path to addons directory", metavar="PATH")
-optparser.add_option('--load', dest='server_wide_modules', default=['web'], action='append',
- help="Load an additional module before login (by default only 'web' is loaded)", metavar="MODULE")
-
-server_options = optparse.OptionGroup(optparser, "Server configuration")
-server_options.add_option("-p", "--port", dest="socket_port", default=8002,
- help="listening port", type="int", metavar="NUMBER")
-server_options.add_option('--reloader', dest='reloader',
- default=False, action='store_true',
- help="Reload application when python files change")
-server_options.add_option('--no-serve-static', dest='serve_static',
- default=True, action='store_false',
- help="Do not serve static files via this server")
-server_options.add_option('--multi-threaded', dest='threaded',
- default=False, action='store_true',
- help="Spawn one thread per HTTP request")
-server_options.add_option('--proxy-mode', dest='proxy_mode',
- default=False, action='store_true',
- help="Enable correct behavior when behind a reverse proxy")
-optparser.add_option_group(server_options)
-
-logging_opts = optparse.OptionGroup(optparser, "Logging")
-logging_opts.add_option("--log-level", dest="log_level", type="choice",
- default='debug', help="Global logging level", metavar="LOG_LEVEL",
- choices=['debug', 'info', 'warning', 'error', 'critical'])
-logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(os.path.dirname(__file__), "logging.json"),
- help="Logging configuration file", metavar="FILE")
-optparser.add_option_group(logging_opts)
-
-testing_opts = optparse.OptionGroup(optparser, "Testing")
-testing_opts.add_option('--test-mode', dest='test_mode',
- action='store_true', default=False,
- help="Starts test mode, which provides a few"
- " (utterly unsafe) APIs for testing purposes and"
- " sets up a special connector which always raises"
- " errors on tentative server access. These errors"
- " serialize RPC query information (service,"
- " method, arguments list) in the fault_code"
- " attribute of the error object returned to the"
- " client. This lets javascript code assert the" \
- " XMLRPC consequences of its queries.")
-optparser.add_option_group(testing_opts)
-
-if __name__ == "__main__":
- (options, args) = optparser.parse_args(sys.argv[1:])
-
- if not options.addons_path:
- path_root = os.path.dirname(os.path.abspath(__file__))
- path_addons = os.path.join(path_root, 'addons')
- if os.path.exists(path_addons):
- options.addons_path.append(path_addons)
-
- options.addons_path = [
- path[:-1] if path[-1] in r'\/' else path
- for path in options.addons_path
- if os.path.exists(path)
- ]
-
- for path_addons in options.addons_path:
- if path_addons not in sys.path:
- sys.path.insert(0, path_addons)
-
- try:
- import web.common.http
- except ImportError:
- optparser.error('Error Importing base web module. Check correctness of --addons-path.')
-
- options.backend = 'xmlrpc'
- os.environ["TZ"] = "UTC"
-
- if options.test_mode:
- import web.test_support
- import web.test_support.controllers
- options.connector = web.test_support.TestConnector()
- logging.getLogger('werkzeug').setLevel(logging.WARNING)
-
- if sys.version_info >= (2, 7) and os.path.exists(options.log_config):
- with open(options.log_config) as file:
- dct = json.load(file)
- logging.config.dictConfig(dct)
- logging.getLogger().setLevel(getattr(logging, options.log_level.upper()))
- else:
- logging.basicConfig(level=getattr(logging, options.log_level.upper()))
-
- app = web.common.http.Root(options)
-
- if options.proxy_mode:
- app = werkzeug.contrib.fixers.ProxyFix(app)
-
- werkzeug.serving.run_simple(
- '0.0.0.0', options.socket_port, app,
- use_reloader=options.reloader, threaded=options.threaded)
-
diff --git a/openerp-web.cfg b/openerp-web.cfg
deleted file mode 100644
index 424d1a8c082..00000000000
--- a/openerp-web.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[global]
-
-
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 18d341039ea..00000000000
--- a/setup.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import glob, os, re, setuptools, sys
-from os.path import join, isfile
-
-# List all data files
-def data():
- files = []
- for root, dirnames, filenames in os.walk('openerp'):
- for filename in filenames:
- if not re.match(r'.*(\.pyc|\.pyo|\~)$',filename):
- files.append(os.path.join(root, filename))
- d = {}
- for v in files:
- k=os.path.dirname(v)
- if k in d:
- d[k].append(v)
- else:
- d[k]=[v]
- r = d.items()
- if os.name == 'nt':
- r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*')))
-
- import babel
- r.append(("localedata",
- glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata" , '*'))))
-
- return r
-
-def gen_manifest():
- file_list="\n".join(data())
- open('MANIFEST','w').write(file_list)
-
-if os.name == 'nt':
- sys.path.append("C:\Microsoft.VC90.CRT")
-
-def py2exe_options():
- if os.name == 'nt':
- import py2exe
- return {
- "console" : [ { "script": "openerp-server", "icon_resources": [(1, join("install","openerp-icon.ico"))], }],
- 'options' : {
- "py2exe": {
- "skip_archive": 1,
- "optimize": 2,
- "dist_dir": 'dist',
- "packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "email", "encodings", "imaplib", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ],
- "excludes" : ["Tkconstants","Tkinter","tcl"],
- }
- }
- }
- else:
- return {}
-
-execfile(join(os.path.dirname(__file__), 'openerp', 'release.py'))
-
-setuptools.setup(
- name = 'openerp',
- version = version,
- description = description,
- long_description = long_desc,
- url = url,
- author = author,
- author_email = author_email,
- classifiers = filter(None, classifiers.split("\n")),
- license = license,
- scripts = ['openerp-server'],
- data_files = data(),
- packages = setuptools.find_packages(),
- dependency_links = ['http://download.gna.org/pychart/'],
- #include_package_data = True,
- install_requires = [
- 'pychart',
- 'babel',
- 'docutils',
- 'feedparser',
- 'gdata',
- 'lxml < 3',
- 'mako',
- 'psutil',
- 'psycopg2',
- 'pydot',
- 'python-dateutil < 2',
- 'python-ldap',
- 'python-openid',
- 'pytz',
- 'pywebdav',
- 'pyyaml',
- 'reportlab',
- 'simplejson',
- 'vatnumber',
- 'vobject',
- 'werkzeug',
- 'xlwt',
- 'zsi',
- ],
- extras_require = {
- 'SSL' : ['pyopenssl'],
- },
- **py2exe_options()
-)
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: