From fec027e83203e6705f690469cd39101e4eba3b06 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 15 Jan 2013 11:40:15 +0100 Subject: [PATCH 001/105] [IMP] reorder modules menus bzr revid: chs@openerp.com-20130115104015-5ytx00t0b5230aaf --- openerp/addons/base/module/module_view.xml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/openerp/addons/base/module/module_view.xml b/openerp/addons/base/module/module_view.xml index 3a43a2591ba..3923c9de3bd 100644 --- a/openerp/addons/base/module/module_view.xml +++ b/openerp/addons/base/module/module_view.xml @@ -171,23 +171,18 @@ - - Modules ir.module.module form kanban,tree,form -

No module found!

You should try others search criteria.

- + @@ -195,20 +190,14 @@ Apps apps - - + Updates apps.updates {} - - + From 7e1a76cdd846a8c455d402fcd76d8baa58a9b2f8 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 15 Jan 2013 11:40:47 +0100 Subject: [PATCH 002/105] [FIX] setup.py: correct windows install bzr revid: chs@openerp.com-20130115104047-6hrl825bn5pkmoo6 --- setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup.py b/setup.py index b144924d022..e4e6001d302 100755 --- a/setup.py +++ b/setup.py @@ -43,6 +43,12 @@ def data(): base = os.path.join('pytz', root[len(tzdir) + 1:]) r[base] = [os.path.join(root, f) for f in filenames] + import docutils + dudir = os.path.dirname(docutils.__file__) + for root, _, filenames in os.walk(dudir): + base = os.path.join('docutils', root[len(dudir) + 1:]) + r[base] = [os.path.join(root, f) for f in filenames if not f.endswith(('.py', '.pyc', '.pyo'))] + return r.items() def gen_manifest(): From e2639618f666605e385befb578db3f889a8c035f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 17 Jan 2013 10:27:22 +0100 Subject: [PATCH 003/105] [DOC] remove openerpdev intersphinx, add ref to module doc bzr revid: xmo@openerp.com-20130117092722-6rfhkcu4igrgeo06 --- addons/web/doc/conf.py | 1 - addons/web/doc/module.rst | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web/doc/conf.py b/addons/web/doc/conf.py index 54434190707..7fe38022ab1 100644 --- a/addons/web/doc/conf.py +++ b/addons/web/doc/conf.py @@ -253,6 +253,5 @@ todo_include_todos = True intersphinx_mapping = { 'python': ('http://docs.python.org/', None), 'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None), - 'openerpdev': ('http://doc.openerp.com/trunk/developers', None), 'openerpcommand': ('http://doc.openerp.com/trunk/developers/command', None), } diff --git a/addons/web/doc/module.rst b/addons/web/doc/module.rst index 1bdb7c14799..8306e885f65 100644 --- a/addons/web/doc/module.rst +++ b/addons/web/doc/module.rst @@ -1,3 +1,5 @@ +.. _module: + Building an OpenERP Web module ============================== From 3574038ffdf78e8c63430ecc92a4f2d916207fda Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 17 Jan 2013 14:29:24 +0100 Subject: [PATCH 004/105] [FIX] apps: install_from_url: fix install condition + better logging bzr revid: chs@openerp.com-20130117132924-ejzbkh5przqi6hwf --- openerp/addons/base/module/module.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index 9e8b68451c5..123bf4124ed 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2012 OpenERP S.A. (). +# Copyright (C) 2004-2013 OpenERP S.A. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -656,6 +656,7 @@ class module(osv.osv): def install_from_urls(self, cr, uid, urls, context=None): OPENERP = 'openerp' tmp = tempfile.mkdtemp() + _logger.debug('Install from url: %r', urls) try: # 1. Download & unzip missing modules for module_name, url in urls.items(): @@ -672,12 +673,13 @@ class module(osv.osv): zipfile.ZipFile(StringIO(content)).extractall(tmp) assert os.path.isdir(os.path.join(tmp, module_name)) - # 2a. Copy/Replace module source in addons path + # 2a. Copy/Replace module source in addons path for module_name, url in urls.items(): if module_name == OPENERP or not url: continue # OPENERP is special case, handled below, and no URL means local module module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False) bck = backup(module_path, False) + _logger.info('Copy downloaded module `%s` to `%s`', module_name, module_path) shutil.move(os.path.join(tmp, module_name), module_path) if bck: shutil.rmtree(bck) @@ -697,15 +699,22 @@ class module(osv.osv): # then replace the server by the new "base" module server_dir = openerp.tools.config['root_path'] # XXX or dirname() bck = backup(server_dir) + _logger.info('Copy downloaded module `openerp` to `%s`', server_dir) shutil.move(os.path.join(tmp, OPENERP), server_dir) #if bck: # shutil.rmtree(bck) self.update_list(cr, uid, context=context) - ids = self.search(cr, uid, [('name', 'in', urls.keys())], context=context) - if self.search_count(cr, uid, [('id', 'in', ids), ('state', '=', 'installed')], context=context): - # if any to update + with_urls = [m for m, u in urls.items() if u] + downloaded_ids = self.search(cr, uid, [('name', 'in', with_urls)], context=context) + already_installed = self.search(cr, uid, [('id', 'in', downloaded_ids), ('state', '=', 'installed')], context=context) + + to_install_ids = self.search(cr, uid, [('name', 'in', urls.keys()), ('state', '=', 'uninstalled')], context=context) + post_install_action = self.button_immediate_install(cr, uid, to_install_ids, context=context) + + if already_installed: + # in this case, force server restart to reload python code... cr.commit() openerp.service.restart_server() return { @@ -713,7 +722,7 @@ class module(osv.osv): 'tag': 'home', 'params': {'wait': True}, } - return self.button_immediate_install(cr, uid, ids, context=context) + return post_install_action finally: shutil.rmtree(tmp) From 4857a8a020d81ebd375122fb478726103c3f2393 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 17 Jan 2013 14:51:08 +0100 Subject: [PATCH 005/105] [FIX] module loading: call adapt_version() instead of computing it ourself bzr revid: chs@openerp.com-20130117135108-a4wt4wtbtfdwiui3 --- openerp/modules/loading.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index fb68642f137..638a40c9fa8 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). -# Copyright (C) 2010-2012 OpenERP s.a. (). +# Copyright (C) 2010-2013 OpenERP s.a. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -36,14 +36,12 @@ import openerp.modules.graph import openerp.modules.migration import openerp.osv as osv import openerp.pooler as pooler -import openerp.release as release import openerp.tools as tools from openerp import SUPERUSER_ID -from openerp import SUPERUSER_ID from openerp.tools.translate import _ from openerp.modules.module import initialize_sys_path, \ - load_openerp_module, init_module_models + load_openerp_module, init_module_models, adapt_version _logger = logging.getLogger(__name__) @@ -213,7 +211,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= migrations.migrate_module(package, 'post') - ver = release.major_version + '.' + package.data['version'] + ver = adapt_version(package.data['version']) # Set new modules and dependencies modobj.write(cr, SUPERUSER_ID, [module_id], {'state': 'installed', 'latest_version': ver}) # Update translations for all installed languages From 3276861ff6125d02e08b5c5b12b75925e056c4bc Mon Sep 17 00:00:00 2001 From: "Xavier Fernandez http://www.smile.fr" <> Date: Wed, 28 Aug 2013 17:21:17 +0200 Subject: [PATCH 007/105] [FIX] port Xavier Fernandez's fix for Bug #971412: Translations export has no fixed order lp bug: https://launchpad.net/bugs/971412 fixed bzr revid: ls@numerigraphe.com-20130828152117-e1h0yljgbn538v9o --- openerp/tools/translate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 41113b8ccd9..53762b87970 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -458,7 +458,8 @@ def trans_export(lang, modules, buffer, format, cr): row.setdefault('tnrs', []).append((type, name, res_id)) row.setdefault('comments', set()).update(comments) - for src, row in grouped_rows.items(): + for src in sorted(grouped_rows): + row = grouped_rows[src] if not lang: # translation template, so no translation value row['translation'] = '' From 1f3a482f4fc309262802b980e143234a56b9503d Mon Sep 17 00:00:00 2001 From: Lionel Sausin Date: Thu, 29 Aug 2013 09:50:35 +0200 Subject: [PATCH 008/105] [REF] avoid the lookup in sorted translation export as suggested by Guewen Baconnier @ Camptocamp bzr revid: ls@numerigraphe.com-20130829075035-lml81a7bbn66c8qu --- openerp/tools/translate.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 53762b87970..6e9eb50f162 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -458,8 +458,7 @@ def trans_export(lang, modules, buffer, format, cr): row.setdefault('tnrs', []).append((type, name, res_id)) row.setdefault('comments', set()).update(comments) - for src in sorted(grouped_rows): - row = grouped_rows[src] + for src, row in sorted(grouped_rows.items()): if not lang: # translation template, so no translation value row['translation'] = '' From 855836e96e42f1be3075eaa87628184f52112b80 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 27 Feb 2014 13:56:48 +0100 Subject: [PATCH 009/105] [IMP] Move the netsvc.dispatch_rpc function from netsvc to openerp.http bzr revid: stw@openerp.com-20140227125648-kunaefr22y28honx --- openerp/http.py | 96 +++++++++++++++++++++++++++++++--- openerp/netsvc.py | 86 ------------------------------ openerp/service/wsgi_server.py | 2 +- openerp/tools/__init__.py | 2 +- openerp/tools/debugger.py | 8 +++ 5 files changed, 98 insertions(+), 96 deletions(-) create mode 100644 openerp/tools/debugger.py diff --git a/openerp/http.py b/openerp/http.py index bcd7511d769..ea1e82ed4c6 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -21,8 +21,10 @@ import time import traceback import urlparse import warnings +from pprint import pformat import babel.core +import psutil import psycopg2 import simplejson import werkzeug.contrib.sessions @@ -34,6 +36,7 @@ import werkzeug.wrappers import werkzeug.wsgi import openerp +import openerp.netsvc from openerp.service import security, model as service_model import openerp.tools @@ -50,6 +53,83 @@ request = _request_stack() A global proxy that always redirect to the current request object. """ +def replace_request_password(args): + # password is always 3rd argument in a request, we replace it in RPC logs + # so it's easier to forward logs for diagnostics/debugging purposes... + if len(args) > 2: + args = list(args) + args[2] = '*' + return tuple(args) + +def log(logger, level, prefix, msg, depth=None): + indent='' + indent_after=' '*len(prefix) + for line in (prefix+pformat(msg, depth=depth)).split('\n'): + logger.log(level, indent+line) + indent=indent_after + +def dispatch_rpc(service_name, method, params): + """ Handle a RPC call. + + This is pure Python code, the actual marshalling (from/to XML-RPC) is done + in a upper layer. + """ + try: + rpc_request = logging.getLogger(__name__ + '.rpc.request') + rpc_response = logging.getLogger(__name__ + '.rpc.response') + rpc_request_flag = rpc_request.isEnabledFor(logging.DEBUG) + rpc_response_flag = rpc_response.isEnabledFor(logging.DEBUG) + if rpc_request_flag or rpc_response_flag: + start_time = time.time() + start_rss, start_vms = 0, 0 + start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info() + if rpc_request and rpc_response_flag: + log(rpc_request, logging.DEBUG, '%s.%s' % (service_name, method), replace_request_password(params)) + + threading.current_thread().uid = None + threading.current_thread().dbname = None + if service_name == 'common': + dispatch = openerp.service.common.dispatch + elif service_name == 'db': + dispatch = openerp.service.db.dispatch + elif service_name == 'object': + dispatch = openerp.service.model.dispatch + elif service_name == 'report': + dispatch = openerp.service.report.dispatch + else: + dispatch = openerp.service.wsgi_server.rpc_handlers.get(service_name) + result = dispatch(method, params) + + if rpc_request_flag or rpc_response_flag: + end_time = time.time() + end_rss, end_vms = 0, 0 + end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info() + logline = '%s.%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (service_name, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) + if rpc_response_flag: + log(rpc_response, logging.DEBUG, logline, result) + else: + log(rpc_request, logging.DEBUG, logline, replace_request_password(params), depth=1) + + return result + except openerp.osv.orm.except_orm: + raise + except openerp.exceptions.AccessError: + raise + except openerp.exceptions.AccessDenied: + raise + except openerp.exceptions.Warning: + raise + except openerp.exceptions.RedirectWarning: + raise + except openerp.exceptions.DeferredException, e: + _logger.exception(openerp.tools.exception_to_unicode(e)) + openerp.tools.debugger.post_mortem(openerp.tools.config, e.traceback) + raise + except Exception, e: + _logger.exception(openerp.tools.exception_to_unicode(e)) + openerp.tools.debugger.post_mortem(openerp.tools.config, sys.exc_info()) + raise + def local_redirect(path, query=None, keep_hash=False, forward_debug=True, code=303): url = path if not query: @@ -618,7 +698,7 @@ class SessionExpiredException(Exception): class Service(object): """ .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. + Use ``dispatch_rpc()`` instead. """ def __init__(self, session, service_name): self.session = session @@ -626,7 +706,7 @@ class Service(object): def __getattr__(self, method): def proxy_method(*args): - result = openerp.netsvc.dispatch_rpc(self.service_name, method, args) + result = dispatch_rpc(self.service_name, method, args) return result return proxy_method @@ -699,7 +779,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): HTTP_HOST=wsgienv['HTTP_HOST'], REMOTE_ADDR=wsgienv['REMOTE_ADDR'], ) - uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env]) + uid = dispatch_rpc('common', 'authenticate', [db, login, password, env]) else: security.check(db, uid, password) self.db = db @@ -803,14 +883,14 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): def send(self, service_name, method, *args): """ .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. + Use ``dispatch_rpc()`` instead. """ - return openerp.netsvc.dispatch_rpc(service_name, method, args) + return dispatch_rpc(service_name, method, args) def proxy(self, service): """ .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. + Use ``dispatch_rpc()`` instead. """ return Service(self, service) @@ -1184,7 +1264,7 @@ class Root(object): return request.registry['ir.http'].routing_map() def db_list(force=False, httprequest=None): - dbs = openerp.netsvc.dispatch_rpc("db", "list", [force]) + dbs = dispatch_rpc("db", "list", [force]) return db_filter(dbs, httprequest=httprequest) def db_filter(dbs, httprequest=None): @@ -1229,7 +1309,7 @@ class CommonController(Controller): @route('/jsonrpc', type='json', auth="none") def jsonrpc(self, service, method, args): """ Method used by client APIs to contact OpenERP. """ - return openerp.netsvc.dispatch_rpc(service, method, args) + return dispatch_rpc(service, method, args) @route('/gen_session_id', type='json', auth="none") def gen_session_id(self): diff --git a/openerp/netsvc.py b/openerp/netsvc.py index 755f6513b25..f19162cde5a 100644 --- a/openerp/netsvc.py +++ b/openerp/netsvc.py @@ -26,10 +26,6 @@ import os import release import sys import threading -import time -import types -from pprint import pformat -import psutil import tools import openerp @@ -188,86 +184,4 @@ def init_alternative_logger(): logger.addHandler(handler) logger.setLevel(logging.ERROR) -def replace_request_password(args): - # password is always 3rd argument in a request, we replace it in RPC logs - # so it's easier to forward logs for diagnostics/debugging purposes... - if len(args) > 2: - args = list(args) - args[2] = '*' - return tuple(args) - -def log(logger, level, prefix, msg, depth=None): - indent='' - indent_after=' '*len(prefix) - for line in (prefix+pformat(msg, depth=depth)).split('\n'): - logger.log(level, indent+line) - indent=indent_after - -def dispatch_rpc(service_name, method, params): - """ Handle a RPC call. - - This is pure Python code, the actual marshalling (from/to XML-RPC) is done - in a upper layer. - """ - try: - rpc_request = logging.getLogger(__name__ + '.rpc.request') - rpc_response = logging.getLogger(__name__ + '.rpc.response') - rpc_request_flag = rpc_request.isEnabledFor(logging.DEBUG) - rpc_response_flag = rpc_response.isEnabledFor(logging.DEBUG) - if rpc_request_flag or rpc_response_flag: - start_time = time.time() - start_rss, start_vms = 0, 0 - start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info() - if rpc_request and rpc_response_flag: - log(rpc_request,logging.DEBUG,'%s.%s'%(service_name,method), replace_request_password(params)) - - threading.current_thread().uid = None - threading.current_thread().dbname = None - if service_name == 'common': - dispatch = openerp.service.common.dispatch - elif service_name == 'db': - dispatch = openerp.service.db.dispatch - elif service_name == 'object': - dispatch = openerp.service.model.dispatch - elif service_name == 'report': - dispatch = openerp.service.report.dispatch - else: - dispatch = openerp.service.wsgi_server.rpc_handlers.get(service_name) - result = dispatch(method, params) - - if rpc_request_flag or rpc_response_flag: - end_time = time.time() - end_rss, end_vms = 0, 0 - end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info() - logline = '%s.%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (service_name, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) - if rpc_response_flag: - log(rpc_response,logging.DEBUG, logline, result) - else: - log(rpc_request,logging.DEBUG, logline, replace_request_password(params), depth=1) - - return result - except openerp.osv.orm.except_orm: - raise - except openerp.exceptions.AccessError: - raise - except openerp.exceptions.AccessDenied: - raise - except openerp.exceptions.Warning: - raise - except openerp.exceptions.RedirectWarning: - raise - except openerp.exceptions.DeferredException, e: - _logger.exception(tools.exception_to_unicode(e)) - post_mortem(e.traceback) - raise - except Exception, e: - _logger.exception(tools.exception_to_unicode(e)) - post_mortem(sys.exc_info()) - raise - -def post_mortem(info): - if tools.config['debug_mode'] and isinstance(info[2], types.TracebackType): - import pdb - pdb.post_mortem(info[2]) - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/service/wsgi_server.py b/openerp/service/wsgi_server.py index 506a07d0295..b31736ba998 100644 --- a/openerp/service/wsgi_server.py +++ b/openerp/service/wsgi_server.py @@ -72,7 +72,7 @@ def xmlrpc_return(start_response, service, method, params, string_faultcode=Fals # This also mimics SimpleXMLRPCDispatcher._marshaled_dispatch() for # exception handling. try: - result = openerp.netsvc.dispatch_rpc(service, method, params) + result = openerp.http.dispatch_rpc(service, method, params) response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None) except Exception, e: if string_faultcode: diff --git a/openerp/tools/__init__.py b/openerp/tools/__init__.py index 18dab088440..6ad2cca4724 100644 --- a/openerp/tools/__init__.py +++ b/openerp/tools/__init__.py @@ -34,7 +34,7 @@ from yaml_import import * from sql import * from float_utils import * from mail import * - +from debugger import * # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tools/debugger.py b/openerp/tools/debugger.py new file mode 100644 index 00000000000..293d4052513 --- /dev/null +++ b/openerp/tools/debugger.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright: 2014 - OpenERP S.A. +import types + +def post_mortem(config, info): + if config['debug_mode'] and isinstance(info[2], types.TracebackType): + import pdb + pdb.post_mortem(info[2]) From 3f779a5cae7bd4ca0399cf06e331f5047ec0e343 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 28 Feb 2014 17:10:55 +0100 Subject: [PATCH 010/105] [IMP] Add the ir_logging object where we will store the log messages (exception, error and warning) bzr revid: stw@openerp.com-20140228161055-98897xl1r46mf9mr --- openerp/addons/base/ir/__init__.py | 1 + openerp/addons/base/ir/ir_logging.py | 52 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 openerp/addons/base/ir/ir_logging.py diff --git a/openerp/addons/base/ir/__init__.py b/openerp/addons/base/ir/__init__.py index b3df5bcbccb..9aa1e7c42a2 100644 --- a/openerp/addons/base/ir/__init__.py +++ b/openerp/addons/base/ir/__init__.py @@ -39,6 +39,7 @@ import ir_mail_server import ir_fields import ir_qweb import ir_http +import ir_logging # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_logging.py b/openerp/addons/base/ir/ir_logging.py new file mode 100644 index 00000000000..66708aa729a --- /dev/null +++ b/openerp/addons/base/ir/ir_logging.py @@ -0,0 +1,52 @@ +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014 OpenERP SA () +# +# 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 . +# +############################################################################## +import logging + +from openerp.osv import osv, fields +from openerp.tools.translate import _ + +class ir_logging(osv.Model): + _name = 'ir.logging' + + EXCEPTIONS_TYPE = [ + ('client', 'Client'), + ('server', 'Server') + ] + + _columns = { + 'name': fields.char('Name', required=True), + 'type': fields.selection(EXCEPTIONS_TYPE, string='Type', required=True, select=True), + 'dbname': fields.char('Database Name'), + 'level': fields.char('Level'), + 'message': fields.text('Message', required=True), + 'exception': fields.text('Exception'), + 'path': fields.char('Path', required=True), + 'func': fields.char('Function', required=True), + 'line': fields.char('Line', required=True), + } + + def call_function(self, cr, uid, ids, context=None): + logger = logging.getLogger() + logger.error("I think there is an error") + try: + raise Exception("I want to kill your process...") + except Exception, ex: + logger.exception("Please log me into the database") + return True \ No newline at end of file From 047d071a48630e3db2922d404d3d7f5395a88245 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 28 Feb 2014 17:11:47 +0100 Subject: [PATCH 011/105] [IMP] Add a new Logging Handler, where we will store in the database of OpenERP. this database can be overrided via the --log-pgsql-database bzr revid: stw@openerp.com-20140228161147-s9nnrfq2tc94vq5p --- openerp/http.py | 12 ++--- openerp/loggers/__init__.py | 1 + openerp/loggers/handlers.py | 100 ++++++++++++++++++++++++++++++++++++ openerp/netsvc.py | 51 ++++++++++++++---- openerp/tools/config.py | 24 ++++++--- 5 files changed, 162 insertions(+), 26 deletions(-) create mode 100644 openerp/loggers/__init__.py create mode 100644 openerp/loggers/handlers.py diff --git a/openerp/http.py b/openerp/http.py index ea1e82ed4c6..92fe03a6232 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -111,15 +111,9 @@ def dispatch_rpc(service_name, method, params): log(rpc_request, logging.DEBUG, logline, replace_request_password(params), depth=1) return result - except openerp.osv.orm.except_orm: - raise - except openerp.exceptions.AccessError: - raise - except openerp.exceptions.AccessDenied: - raise - except openerp.exceptions.Warning: - raise - except openerp.exceptions.RedirectWarning: + except (openerp.osv.orm.except_orm, openerp.exceptions.AccessError, \ + openerp.exceptions.AccessDenied, openerp.exceptions.Warning, \ + openerp.exceptions.RedirectWarning): raise except openerp.exceptions.DeferredException, e: _logger.exception(openerp.tools.exception_to_unicode(e)) diff --git a/openerp/loggers/__init__.py b/openerp/loggers/__init__.py new file mode 100644 index 00000000000..19b76b55b64 --- /dev/null +++ b/openerp/loggers/__init__.py @@ -0,0 +1 @@ +from handlers import PostgreSQLHandler \ No newline at end of file diff --git a/openerp/loggers/handlers.py b/openerp/loggers/handlers.py new file mode 100644 index 00000000000..763aa079195 --- /dev/null +++ b/openerp/loggers/handlers.py @@ -0,0 +1,100 @@ +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014 OpenERP SA () +# +# 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 . +# +############################################################################## + +import contextlib +import datetime +import logging +import threading + +import psycopg2 + +from openerp import tools + +# The PostgreSQL Handler for the logging module, will be used by OpenERP to store the logs +# in the database, --log-pgsql-database=YOUR_DBNAME +# By default the system will use the current database + +class PostgreSQLHandler(logging.Handler): + @contextlib.contextmanager + def create_connection(self): + db_name = None + + db_name_from_cli = tools.config['log_pgsql_database'] + if not db_name_from_cli: + # If there is no database, and only in this case, we are going to use the database + # from the current thread and create a connection to this database. + + current_thread = threading.current_thread() + + db_name_from_thread = getattr(current_thread, 'dbname', None) + if isinstance(db_name_from_thread, basestring): + db_name = db_name_from_thread + else: + db_name = db_name_from_cli + + if not db_name: + return + + parameters = { + 'user': tools.config['db_user'] or None, + 'password': tools.config['db_password'] or None, + 'host': tools.config['db_host'] or None, + 'port': tools.config['db_port'] or None, + 'database': db_name, + } + try: + connection = psycopg2.connect(**parameters) + + if connection: + yield connection + except Exception, ex: # Use a specific exception + print ex + + def emit(self, record): + # We use a context manager to be tolerant to the errors (error of connections,...) + with self.create_connection() as conn: + exception = False + if record.exc_info: + exception = record.exc_text + + now = datetime.datetime.utcnow() + + current_thread = threading.current_thread() + uid = getattr(current_thread, 'uid', False) + dbname = getattr(current_thread, 'dbname', False) + + parameters = ( + now, uid, now, uid, 'server', dbname, record.name, + logging.getLevelName(record.levelno), record.msg, exception, + record.filename, record.funcName, record.lineno + ) + + with conn.cursor() as cursor: + cursor.execute(""" + INSERT INTO ir_logging( + create_date, create_uid, write_date, write_uid, + type, dbname, name, level, message, exception, path, func, + line + ) + VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + """, + parameters + ) + conn.commit() diff --git a/openerp/netsvc.py b/openerp/netsvc.py index f19162cde5a..250cd52a1d1 100644 --- a/openerp/netsvc.py +++ b/openerp/netsvc.py @@ -29,6 +29,7 @@ import threading import tools import openerp +import openerp.loggers _logger = logging.getLogger(__name__) @@ -85,6 +86,19 @@ class ColoredFormatter(DBFormatter): record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname) return DBFormatter.format(self, record) +import platform +def is_posix_operating_system(): + return os.name == 'posix' + +def is_windows_operating_system(): + return os.name == 'nt' + +def is_linux_operating_system(): + return is_posix_operating_system() and platform.system() == 'Linux' + +def is_macosx_operating_system(): + return is_posix_operating_system() and platform.system() == 'Darwin' + def init_logger(): from tools.translate import resetlocale resetlocale() @@ -94,24 +108,32 @@ def init_logger(): if tools.config['syslog']: # SysLog Handler - if os.name == 'nt': + if is_windows_operating_system(): handler = logging.handlers.NTEventLogHandler("%s %s" % (release.description, release.version)) - else: + elif is_linux_operating_system(): handler = logging.handlers.SysLogHandler('/dev/log') - format = '%s %s' % (release.description, release.version) \ - + ':%(dbname)s:%(levelname)s:%(name)s:%(message)s' + elif is_macosx_operating_system(): # There is no /dev/log on OSX + handler = logging.handlers.SysLogHandler('/var/run/log') + else: + raise Exception("There is no syslog handler for this Operating System: %s", platform.system()) + + format = '%s %s' % (release.description, release.version) + ':%(dbname)s:%(levelname)s:%(name)s:%(message)s' elif tools.config['logfile']: # LogFile Handler logf = tools.config['logfile'] try: + # We check we have the right location for the log files dirname = os.path.dirname(logf) if dirname and not os.path.isdir(dirname): os.makedirs(dirname) + if tools.config['logrotate'] is not False: - handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30) - elif os.name == 'posix': + handler = logging.handlers.TimedRotatingFileHandler(filename=logf, when='D', interval=1, backupCount=30) + + elif is_posix_operating_system(): handler = logging.handlers.WatchedFileHandler(logf) + else: handler = logging.handlers.FileHandler(logf) except Exception: @@ -125,12 +147,14 @@ def init_logger(): # behind Apache with mod_wsgi, handler.stream will have type mod_wsgi.Log, # which has no fileno() method. (mod_wsgi.Log is what is being bound to # sys.stderr when the logging.StreamHandler is being constructed above.) - if isinstance(handler, logging.StreamHandler) \ - and hasattr(handler.stream, 'fileno') \ - and os.isatty(handler.stream.fileno()): + def has_fileno(stream): + return hasattr(stream, 'fileno') and os.isatty(stream.fileno()) + + if isinstance(handler, logging.StreamHandler) and has_fileno(handler.stream): formatter = ColoredFormatter(format) else: formatter = DBFormatter(format) + handler.setFormatter(formatter) # Configure handlers @@ -141,7 +165,9 @@ def init_logger(): logging_configurations = DEFAULT_LOG_CONFIGURATION + pseudo_config + logconfig for logconfig_item in logging_configurations: loggername, level = logconfig_item.split(':') + level = getattr(logging, level, logging.INFO) + logger = logging.getLogger(loggername) logger.handlers = [] logger.setLevel(level) @@ -149,6 +175,13 @@ def init_logger(): if loggername != '': logger.propagate = False + # magic ;-) + # we manage the connection in the postgresqlhandler + postgresqlHandler = openerp.loggers.handlers.PostgreSQLHandler() + postgresqlHandler.setLevel(logging.WARNING) + logger = logging.getLogger() + logger.addHandler(postgresqlHandler) + for logconfig_item in logging_configurations: _logger.debug('logger level set: "%s"', logconfig_item) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 07435075f8d..ab1eab8a52a 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -72,9 +72,10 @@ class configmanager(object): } # Not exposed in the configuration file. - self.blacklist_for_save = set( - ['publisher_warranty_url', 'load_language', 'root_path', - 'init', 'save', 'config', 'update', 'stop_after_init']) + self.blacklist_for_save = set([ + 'publisher_warranty_url', 'load_language', 'root_path', + 'init', 'save', 'config', 'update', 'stop_after_init' + ]) # dictionary mapping option destination (keys in self.options) to MyOptions. self.casts = {} @@ -83,7 +84,10 @@ class configmanager(object): self.config_file = fname self.has_ssl = check_ssl() - self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')]) + self._LOGLEVELS = dict([ + (getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) + for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET') + ]) version = "%s %s" % (release.description, release.version) self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption) @@ -176,12 +180,16 @@ class configmanager(object): group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG') group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.http:DEBUG') group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG') + group.add_option('--log-pgsql-database', dest='log_pgsql_database', help="database where OpenERP will store the exceptions", my_default=False) # For backward-compatibility, map the old log levels to something # quite close. - levels = ['info', 'debug_rpc', 'warn', 'test', 'critical', - 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset'] - group.add_option('--log-level', dest='log_level', type='choice', choices=levels, - my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).') + levels = [ + 'info', 'debug_rpc', 'warn', 'test', 'critical', + 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset' + ] + group.add_option('--log-level', dest='log_level', type='choice', + choices=levels, my_default='info', + help='specify the level of the logging. Accepted values: %s (deprecated option).' % (levels,)) parser.add_option_group(group) From cff80a9a0c6c3824b138db533665eb1bc1031bd4 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 10 Mar 2014 12:11:37 +0100 Subject: [PATCH 012/105] [IMP] Add views for the ir.logging object bzr revid: stw@openerp.com-20140310111137-04axbgdajd9jloxg --- openerp/addons/base/__openerp__.py | 1 + openerp/addons/base/ir/ir_logging_view.xml | 65 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 openerp/addons/base/ir/ir_logging_view.xml diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 3c3c290f986..57ce6b31e32 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -57,6 +57,7 @@ The kernel of OpenERP, needed for all installation. 'ir/ir_values_view.xml', 'ir/osv_memory_autovacuum.xml', 'ir/ir_model_report.xml', + 'ir/ir_logging_view.xml', 'workflow/workflow_view.xml', 'module/module_view.xml', 'module/module_data.xml', diff --git a/openerp/addons/base/ir/ir_logging_view.xml b/openerp/addons/base/ir/ir_logging_view.xml new file mode 100644 index 00000000000..d23e45f1d14 --- /dev/null +++ b/openerp/addons/base/ir/ir_logging_view.xml @@ -0,0 +1,65 @@ + + + + + ir.logging + +
+ + + + + + + + + + + +
+
+
+ + ir.logging + + + + + + + + + + + + + + ir.logging + + + + + + + + + + + + + + + + + + Logging + ir.logging + form + tree,form + + + + + +
+
\ No newline at end of file From d9fcc1604500b6df079cd40beba7c368be992372 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Tue, 11 Mar 2014 18:13:56 +0100 Subject: [PATCH 013/105] [FIX] ir.logging: Raise an internal exception in the case where there is no defined database, but this exception is catched by the _internal_emit method and just ignore it to avoid a crash. Better solution, try to log on the standard handler. bzr revid: stw@openerp.com-20140311171356-jghlh2w2pnxvtu2e --- openerp/loggers/handlers.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/openerp/loggers/handlers.py b/openerp/loggers/handlers.py index 763aa079195..24be46085c8 100644 --- a/openerp/loggers/handlers.py +++ b/openerp/loggers/handlers.py @@ -31,6 +31,9 @@ from openerp import tools # in the database, --log-pgsql-database=YOUR_DBNAME # By default the system will use the current database +class NoDatabaseError(Exception): + pass + class PostgreSQLHandler(logging.Handler): @contextlib.contextmanager def create_connection(self): @@ -50,7 +53,7 @@ class PostgreSQLHandler(logging.Handler): db_name = db_name_from_cli if not db_name: - return + raise NoDatabaseError("There is no defined database on this request") parameters = { 'user': tools.config['db_user'] or None, @@ -67,8 +70,7 @@ class PostgreSQLHandler(logging.Handler): except Exception, ex: # Use a specific exception print ex - def emit(self, record): - # We use a context manager to be tolerant to the errors (error of connections,...) + def _internal_emit(self, record): with self.create_connection() as conn: exception = False if record.exc_info: @@ -81,7 +83,7 @@ class PostgreSQLHandler(logging.Handler): dbname = getattr(current_thread, 'dbname', False) parameters = ( - now, uid, now, uid, 'server', dbname, record.name, + now, uid, now, uid, 'server', dbname, record.name, logging.getLevelName(record.levelno), record.msg, exception, record.filename, record.funcName, record.lineno ) @@ -89,12 +91,17 @@ class PostgreSQLHandler(logging.Handler): with conn.cursor() as cursor: cursor.execute(""" INSERT INTO ir_logging( - create_date, create_uid, write_date, write_uid, + create_date, create_uid, write_date, write_uid, type, dbname, name, level, message, exception, path, func, line ) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - """, - parameters - ) + """, parameters) conn.commit() + + def emit(self, record): + # We use a context manager to be tolerant to the errors (error of connections,...) + try: + self._internal_emit(record) + except NoDatabaseError: + pass From 27405c0c83b05e9239eb76550004732776fc5e6a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 11 Mar 2014 21:52:20 +0100 Subject: [PATCH 014/105] [FIX] ensure_db() werkzeug.BaseResponse.url usage leads to encoded hashes loss bzr revid: fme@openerp.com-20140311205220-kk0pal10lodurcst --- addons/web/controllers/main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d13226ca731..0074a88ba9c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -141,7 +141,12 @@ def ensure_db(redirect='/web/database/selector'): # Thus, we redirect the user to the same page but with the session cookie set. # This will force using the database route dispatcher... r = request.httprequest - response = werkzeug.utils.redirect(r.url, 302) + url_redirect = r.base_url + if r.query_string: + # Can't use werkzeug.wrappers.BaseRequest.url with encoded hashes: + # https://github.com/amigrave/werkzeug/commit/b4a62433f2f7678c234cdcac6247a869f90a7eb7 + url_redirect += '?' + r.query_string + response = werkzeug.utils.redirect(url_redirect, 302) request.session.db = db response = r.app.get_response(r, response, explicit_session=False) werkzeug.exceptions.abort(response) From 9684f077358091c0eccf30abb2dfecd83fb0eb4a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 11 Mar 2014 21:53:37 +0100 Subject: [PATCH 015/105] [FIX] signup_url_for_action(), use `redirect` url parameter and bring back `action` in fragment bzr revid: fme@openerp.com-20140311205337-9vasnqx5cudbk3f3 --- addons/auth_signup/res_users.py | 11 ++++++++--- addons/portal/mail_mail.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index e91af657cc2..32aa22b2744 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -20,8 +20,8 @@ ############################################################################## from datetime import datetime, timedelta import random -from urllib import urlencode from urlparse import urljoin +import werkzeug from openerp.addons.base.ir.ir_mail_server import MailDeliveryException from openerp.osv import osv, fields @@ -53,7 +53,7 @@ class res_partner(osv.Model): (not partner.signup_expiration or dt <= partner.signup_expiration) return res - def _get_signup_url_for_action(self, cr, uid, ids, action='login', view_type=None, menu_id=None, res_id=None, model=None, context=None): + def _get_signup_url_for_action(self, cr, uid, ids, action=None, view_type=None, menu_id=None, res_id=None, model=None, context=None): """ generate a signup url for the given partner ids and action, possibly overriding the url state components (menu_id, id, view_type) """ if context is None: @@ -80,6 +80,8 @@ class res_partner(osv.Model): continue # no signup token, no user, thus no signup url! fragment = dict() + if action: + fragment['action'] = action if view_type: fragment['view_type'] = view_type if menu_id: @@ -89,7 +91,10 @@ class res_partner(osv.Model): if res_id: fragment['id'] = res_id - res[partner.id] = urljoin(base_url, "/web/login?%s#%s" % (urlencode(query), urlencode(fragment))) + if fragment: + query['redirect'] = '/web#' + werkzeug.url_encode(fragment) + + res[partner.id] = urljoin(base_url, "/web/login?%s" % werkzeug.url_encode(query)) return res diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py index 18eb15361a3..0fe2081fa45 100644 --- a/addons/portal/mail_mail.py +++ b/addons/portal/mail_mail.py @@ -39,7 +39,7 @@ class mail_mail(osv.Model): if partner and not partner.user_ids: contex_signup = dict(context, signup_valid=True) signup_url = partner_obj._get_signup_url_for_action(cr, SUPERUSER_ID, [partner.id], - action='login', model=mail.model, res_id=mail.res_id, + model=mail.model, res_id=mail.res_id, context=contex_signup)[partner.id] return _("""Access your messages and documents through our Customer Portal""") % signup_url else: From cf68961f668b058952b205b5ba7991ea8c5f6b8b Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 12 Mar 2014 11:32:39 +0100 Subject: [PATCH 016/105] [IMP] /web/login redirects if already logged in bzr revid: fme@openerp.com-20140312103239-deyoc7t00g8kusd1 --- addons/web/controllers/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 0074a88ba9c..c30cd052bd6 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -663,6 +663,9 @@ class Home(http.Controller): def web_login(self, redirect=None, **kw): ensure_db() + if request.httprequest.method == 'GET' and redirect and request.session.uid: + return http.redirect_with_hash(redirect) + values = request.params.copy() if not redirect: redirect = '/web?' + request.httprequest.query_string From 2ed053642abfbf20678c02955b8b0bb575cf8d30 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 12 Mar 2014 12:21:41 +0100 Subject: [PATCH 017/105] [IMP] oaut & signup, redirect if already logged in bzr revid: fme@openerp.com-20140312112141-uvo89w4ovmzq5ozq --- addons/auth_oauth/controllers/main.py | 3 +++ addons/auth_signup/controllers/main.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index 53a6ffd5462..a338927c3a1 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -68,6 +68,9 @@ class OAuthLogin(openerp.addons.web.controllers.main.Home): @http.route() def web_login(self, *args, **kw): + if request.httprequest.method == 'GET' and request.session.uid and request.params.get('redirect'): + # Redirect if already logged in and redirect param is present + return http.redirect_with_hash(request.params.get('redirect')) providers = self.list_providers() response = super(OAuthLogin, self).web_login(*args, **kw) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 08b67108c3f..92ae6364954 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -37,6 +37,9 @@ class AuthSignup(openerp.addons.web.controllers.main.Home): mode = request.params.get('mode') qcontext = request.params.copy() super_response = None + if request.httprequest.method == 'GET' and request.session.uid and request.params.get('redirect'): + # Redirect if already logged in and redirect param is present + return http.redirect_with_hash(request.params.get('redirect')) if request.httprequest.method != 'POST' or mode not in ('reset', 'signup'): # Default behavior is to try to login, which in reset or signup mode in a non-sense. super_response = super(AuthSignup, self).web_login(*args, **kw) From 77e56eea05948e8bde8a42f6cdf1f5a2e10085ae Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Thu, 13 Mar 2014 14:34:59 +0100 Subject: [PATCH 018/105] [REF] Changed seek(0) to flush when manipulating files and reordered the impors bzr revid: sle@openerp.com-20140313133459-y3zvhhjby8pysew1 --- addons/report/controllers/main.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index 0dae5f41288..e057e80beb7 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -41,20 +41,20 @@ import signal import os from distutils.version import LooseVersion - from werkzeug import exceptions from werkzeug.test import Client from werkzeug.wrappers import BaseResponse from werkzeug.datastructures import Headers from reportlab.graphics.barcode import createBarcodeDrawing - - -_logger = logging.getLogger(__name__) try: from pyPdf import PdfFileWriter, PdfFileReader except ImportError: PdfFileWriter = PdfFileReader = None + +_logger = logging.getLogger(__name__) + + class Report(http.Controller): @http.route(['/report//'], type='http', auth='user', website=True, multilang=True) @@ -303,7 +303,7 @@ class Report(http.Controller): # Directly load the document if we have it if save_in_attachment and save_in_attachment['loaded_documents'].get(reporthtml[0]): pdfreport.write(save_in_attachment['loaded_documents'].get(reporthtml[0])) - pdfreport.seek(0) + pdfreport.flush() pdfdocuments.append(pdfreport) continue @@ -312,7 +312,7 @@ class Report(http.Controller): head_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.header.tmp.', dir=tmp_dir, mode='w+') head_file.write(headers[index]) - head_file.seek(0) + head_file.flush() command_arg_local.extend(['--header-html', head_file.name]) # Footer stuff @@ -320,14 +320,14 @@ class Report(http.Controller): foot_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.footer.tmp.', dir=tmp_dir, mode='w+') foot_file.write(footers[index]) - foot_file.seek(0) + foot_file.flush() command_arg_local.extend(['--footer-html', foot_file.name]) # Body stuff content_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.body.tmp.', dir=tmp_dir, mode='w+') content_file.write(reporthtml[1]) - content_file.seek(0) + content_file.flush() try: # If the server is running with only one worker, increase it to two to be able @@ -364,7 +364,7 @@ class Report(http.Controller): _logger.info('The PDF document %s is now saved in the ' 'database' % attachment['name']) - pdfreport.seek(0) + pdfreport.flush() pdfdocuments.append(pdfreport) if headers: @@ -461,7 +461,7 @@ class Report(http.Controller): document.close() merged = StringIO.StringIO() writer.write(merged) - merged.seek(0) + merged.flush() content = merged.read() merged.close() return content From 353bbdc2c3d0e019561eb60aacb36b68b4ed935a Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 14 Mar 2014 17:23:46 +0100 Subject: [PATCH 019/105] [FIX] website: route and redirect: remove all backslash from url bzr revid: chm@openerp.com-20140314162346-25vohj6ayp65tgiy --- addons/website/controllers/main.py | 2 +- addons/website/models/website.py | 2 +- .../static/src/js/website.editor.newpage.js | 2 +- addons/website/static/src/js/website.js | 2 +- .../website/static/src/js/website.mobile.js | 4 +- addons/website_blog/controllers/main.py | 28 ++++---- .../views/website_blog_templates.xml | 2 +- .../controllers/main.py | 20 +++--- .../views/website_crm_partner_assign.xml | 10 +-- addons/website_customer/controllers/main.py | 12 ++-- .../views/website_customer.xml | 16 ++--- addons/website_event/controllers/main.py | 4 +- addons/website_event/views/website_event.xml | 4 +- addons/website_event_sale/controllers/main.py | 2 +- .../static/src/js/website.tour.event_sale.js | 2 +- .../website_event_track/controllers/event.py | 6 +- addons/website_event_track/models/event.py | 8 +-- .../views/website_event.xml | 2 +- addons/website_google_map/controllers/main.py | 4 +- .../static/src/js/google_map.js | 2 +- .../website_hr_recruitment/models/hr_job.py | 2 +- .../views/templates.xml | 4 +- .../controllers/email_designer.py | 2 +- addons/website_mail/controllers/main.py | 4 +- .../static/src/js/website_mail.js | 2 +- addons/website_membership/controllers/main.py | 20 +++--- .../views/website_membership.xml | 14 ++-- addons/website_partner/controllers/main.py | 2 +- addons/website_project/controllers/main.py | 2 +- .../website_project/views/website_project.xml | 2 +- .../static/src/js/website_quotation.js | 4 +- addons/website_sale/controllers/main.py | 64 +++++++++---------- addons/website_sale/models/product.py | 4 +- .../static/src/js/website.tour.sale.js | 12 ++-- .../static/src/js/website_sale.editor.js | 6 +- .../static/src/js/website_sale.js | 4 +- .../static/src/js/website_sale_validate.js | 2 +- addons/website_sale/views/website_sale.xml | 40 ++++++------ .../website_sale_delivery/controllers/main.py | 4 +- 39 files changed, 164 insertions(+), 164 deletions(-) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index eff28b61fcd..abc64fee6e0 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -296,7 +296,7 @@ class Website(openerp.addons.web.controllers.main.Home): #------------------------------------------------------ # Helpers #------------------------------------------------------ - @http.route(['/website/kanban/'], type='http', auth="public", methods=['POST'], website=True) + @http.route(['/website/kanban'], type='http', auth="public", methods=['POST'], website=True) def kanban(self, **post): return request.website.kanban_col(**post) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 966810297ea..d4af418309c 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -248,7 +248,7 @@ class website(osv.osv): pmin = pmax - scope if pmax - scope > 0 else 1 def get_url(page): - _url = "%spage/%s/" % (url, page) if page > 1 else url + _url = "%s/page/%s" % (url, page) if page > 1 else url if url_args: _url = "%s?%s" % (_url, werkzeug.url_encode(url_args)) return _url diff --git a/addons/website/static/src/js/website.editor.newpage.js b/addons/website/static/src/js/website.editor.newpage.js index b475223ecd9..77a086790fa 100644 --- a/addons/website/static/src/js/website.editor.newpage.js +++ b/addons/website/static/src/js/website.editor.newpage.js @@ -48,7 +48,7 @@ } }).then(function (val, field, $dialog) { if (val) { - var url = '/website/add/' + encodeURI(val); + var url = '/website/add' + encodeURI(val); if ($dialog.find('input[type="checkbox"]').is(':checked')) url +="?add_menu=1"; document.location = url; } diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 249058d91fb..eec815ed8c7 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -103,7 +103,7 @@ var page = +$a.attr("href").split(",").pop().split('-')[1]; data['page'] = page; - $.post('/website/kanban/', data, function (col) { + $.post('/website/kanban', data, function (col) { $col.find("> .thumbnail").remove(); $pagination.last().before(col); }); diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index 59c2328e09b..88a558f5094 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -18,9 +18,9 @@ }, start: function () { if (!window.location.origin) { // fix for ie9 - window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '') + '/'; + window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: ''); } - document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; + document.getElementById("mobile-viewport").src = window.location.origin + (window.location.pathname.length ? '/' : '') + window.location.pathname + "#mobile-preview"; this.$el.modal(); }, destroy: function () { diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 2e1477e8f2d..10a43be1bef 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -42,7 +42,7 @@ class WebsiteBlog(http.Controller): @http.route([ '/blog', - '/blog/page//', + '/blog/page/', ], type='http', auth="public", website=True, multilang=True) def blogs(self, page=1): BYPAGE = 60 @@ -50,7 +50,7 @@ class WebsiteBlog(http.Controller): blog_obj = request.registry['blog.post'] total = blog_obj.search(cr, uid, [], count=True, context=context) pager = request.website.pager( - url='/blog/', + url='/blog', total=total, page=page, step=BYPAGE, @@ -63,14 +63,14 @@ class WebsiteBlog(http.Controller): }) @http.route([ - '/blog//', - '/blog//page//', - '/blog//tag//', - '/blog//tag//page//', - '/blog//date//', - '/blog//date//page//', - '/blog//tag//date//', - '/blog//tag//date//page//', + '/blog/', + '/blog//page/', + '/blog//tag/', + '/blog//tag//page/', + '/blog//date/', + '/blog//date//page/', + '/blog//tag//date/', + '/blog//tag//date//page/', ], type='http', auth="public", website=True, multilang=True) def blog(self, blog=None, tag=None, date=None, page=1, **opt): """ Prepare all values to display the blog. @@ -106,13 +106,13 @@ class WebsiteBlog(http.Controller): domain = [] if blog: - path_filter += "%s/" % blog.id + path_filter += "%s" % blog.id domain += [("id", "in", [post.id for post in blog.blog_post_ids])] if tag: - path_filter += 'tag/%s/' % tag.id + path_filter += 'tag/%s' % tag.id domain += [("id", "in", [post.id for post in tag.blog_post_ids])] if date: - path_filter += "date/%s/" % date + path_filter += "date/%s" % date domain += [("create_date", ">=", date.split("_")[0]), ("create_date", "<=", date.split("_")[1])] blog_post_ids = blog_post_obj.search(cr, uid, domain, context=context) @@ -147,7 +147,7 @@ class WebsiteBlog(http.Controller): return request.website.render("website_blog.blog_post_short", values) @http.route([ - '/blogpost//', + '/blogpost/', ], type='http', auth="public", website=True, multilang=True) def blog_post(self, blog_post, tag=None, date=None, page=1, enable_editor=None, **post): """ Prepare all values to display the blog. diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml index 203dcc3bb71..4ca762456f1 100644 --- a/addons/website_blog/views/website_blog_templates.xml +++ b/addons/website_blog/views/website_blog_templates.xml @@ -12,7 +12,7 @@ diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index fbfb1908a14..6d6dda59a99 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -13,21 +13,21 @@ class WebsiteCrmPartnerAssign(http.Controller): _references_per_page = 20 @http.route([ - '/partners/', - '/partners/page//', + '/partners', + '/partners/page/', '/partners/grade/', - '/partners/grade//page//', + '/partners/grade//page/', '/partners/country/', '/partners/country/-', - '/partners/country//page//', - '/partners/country/-/page//', + '/partners/country//page/', + '/partners/country/-/page/', - '/partners/grade//country//', + '/partners/grade//country/', '/partners/grade//country/-', - '/partners/grade//country//page//', - '/partners/grade//country/-/page//', + '/partners/grade//country//page/', + '/partners/grade//country/-/page/', ], type='http', auth="public", website=True, multilang=True) def partners(self, country_id=0, grade_id=0, page=0, **post): @@ -72,7 +72,7 @@ class WebsiteCrmPartnerAssign(http.Controller): partner_ids = partner_obj.search( request.cr, openerp.SUPERUSER_ID, partner_domain, context=request.context) - pager = request.website.pager(url="/partners/", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post) + pager = request.website.pager(url="/partners", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post) # search for partners to display partners_data = partner_obj.search_read(request.cr, openerp.SUPERUSER_ID, @@ -110,7 +110,7 @@ class WebsiteCrmPartnerAssign(http.Controller): } return request.website.render("website_crm_partner_assign.index", values) - @http.route(['/partners//', '/partners/-/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/partners/', '/partners/-'], type='http', auth="public", website=True, multilang=True) def partners_ref(self, partner_id, **post): partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) values = website_partner.get_partner_template_value(partner) diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml index cd2e3cb58bd..56ac22d1eee 100644 --- a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml +++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml @@ -5,7 +5,7 @@ @@ -55,7 +55,7 @@
  • - + @@ -93,11 +93,11 @@
    - +
    - - + -
    @@ -112,7 +112,7 @@

    World Map

    diff --git a/addons/website_customer/controllers/main.py b/addons/website_customer/controllers/main.py index 173b0871dbe..5f0825e132c 100644 --- a/addons/website_customer/controllers/main.py +++ b/addons/website_customer/controllers/main.py @@ -12,12 +12,12 @@ class WebsiteCustomer(http.Controller): _references_per_page = 20 @http.route([ - '/customers/', - '/customers/page//', + '/customers', + '/customers/page/', '/customers/country/', '/customers/country/-', - '/customers/country//page//', - '/customers/country/-/page//', + '/customers/country//page/', + '/customers/country/-/page/', ], type='http', auth="public", website=True, multilang=True) def customers(self, country_id=0, page=0, **post): cr, uid, context = request.cr, request.uid, request.context @@ -62,7 +62,7 @@ class WebsiteCustomer(http.Controller): # pager pager = request.website.pager( - url="/customers/", total=len(partner_ids), page=page, step=self._references_per_page, + url="/customers", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post ) @@ -83,7 +83,7 @@ class WebsiteCustomer(http.Controller): } return request.website.render("website_customer.index", values) - @http.route(['/customers//', '/customers/-/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/customers/', '/customers/-'], type='http', auth="public", website=True, multilang=True) def customer(self, partner_id, **post): partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) values = website_partner.get_partner_template_value(partner) diff --git a/addons/website_customer/views/website_customer.xml b/addons/website_customer/views/website_customer.xml index ad924cba55d..ba89f456e23 100644 --- a/addons/website_customer/views/website_customer.xml +++ b/addons/website_customer/views/website_customer.xml @@ -5,7 +5,7 @@ @@ -44,11 +44,11 @@
  • - + @@ -143,7 +143,7 @@ @@ -158,11 +158,11 @@

    References

    - +
    - +
    diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index eb38e2dbe55..c410536a0b5 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -33,7 +33,7 @@ from openerp import tools import werkzeug.urls class website_event(http.Controller): - @http.route(['/event/', '/event/page/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event', '/event/page/'], type='http', auth="public", website=True, multilang=True) def events(self, page=1, **searches): cr, uid, context = request.cr, request.uid, request.context event_obj = request.registry['event.event'] @@ -134,7 +134,7 @@ class website_event(http.Controller): event_count = event_obj.search( request.cr, request.uid, dom_without("none"), count=True, context=request.context) - pager = request.website.pager(url="/event/", total=event_count, page=page, step=step, scope=5) + pager = request.website.pager(url="/event", total=event_count, page=page, step=step, scope=5) order = 'website_published desc, date_begin' if searches.get('date','all') == 'old': diff --git a/addons/website_event/views/website_event.xml b/addons/website_event/views/website_event.xml index e490b1a0687..1096287bb3b 100644 --- a/addons/website_event/views/website_event.xml +++ b/addons/website_event/views/website_event.xml @@ -50,7 +50,7 @@
  • - + not published

    @@ -243,7 +243,7 @@

    Event not found!

    Sorry, the requested event is not available anymore.

    -

    Return to the event list.

    +

    Return to the event list.

    diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index cc9ddc0ad56..2db874cf5b7 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -78,7 +78,7 @@ class website_event(website_event): order_obj.write(request.cr, SUPERUSER_ID, [order.id], {'order_line': [(4, order_line_id)]}, context=request.context) if not _values: - return request.redirect("/event/%s/" % event_id) + return request.redirect("/event/%s" % event_id) return request.redirect("/shop/checkout") def _add_event(self, event_name="New Event", context={}, **kwargs): diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 3c74cc2317e..c67c412d7d2 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -44,7 +44,7 @@ { title: "Complete checkout", waitFor: '#top_menu .my_cart_quantity:contains(5)', - element: 'form[action="/shop/confirm_order/"] .btn:contains("Confirm")', + element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', onload: function (tour) { if ($("input[name='name']").val() === "") $("input[name='name']").val("website_sale-test-shoptest"); diff --git a/addons/website_event_track/controllers/event.py b/addons/website_event_track/controllers/event.py index d4b7177814d..466e3d37a40 100644 --- a/addons/website_event_track/controllers/event.py +++ b/addons/website_event_track/controllers/event.py @@ -38,7 +38,7 @@ class website_event(http.Controller): return request.website.render("website_event_track.track_view", values) # TODO: not implemented - @http.route(['/event//agenda/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event//agenda'], type='http', auth="public", website=True, multilang=True) def event_agenda(self, event, tag=None, **post): values = { 'event': event, @@ -47,7 +47,7 @@ class website_event(http.Controller): return request.website.render("website_event_track.agenda", values) @http.route([ - '/event//track/', + '/event//track', '/event//track/tag/' ], type='http', auth="public", website=True, multilang=True) def event_tracks(self, event, tag=None, **post): @@ -74,7 +74,7 @@ class website_event(http.Controller): } return request.website.render("website_event_track.tracks", values) - @http.route(['/event//track_proposal/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event//track_proposal'], type='http', auth="public", website=True, multilang=True) def event_track_proposal(self, event, **post): values = { 'event': event } return request.website.render("website_event_track.event_track_proposal", values) diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py index cfba63cfc6b..1c098232a2e 100644 --- a/addons/website_event_track/models/event.py +++ b/addons/website_event_track/models/event.py @@ -142,12 +142,12 @@ class event_event(osv.osv): context = context or {} result = super(event_event, self)._get_new_menu_pages(cr, uid, event, context=context) if event.show_tracks: - result.append( (_('Talks'), '/event/%s/track/' % slug(event))) - result.append( (_('Agenda'), '/event/%s/agenda/' % slug(event))) + result.append( (_('Talks'), '/event/%s/track' % slug(event))) + result.append( (_('Agenda'), '/event/%s/agenda' % slug(event))) if event.blog_id: - result.append( (_('News'), '/blogpost/'+slug(event.blog_ig))) + result.append( (_('News'), '/blogpost'+slug(event.blog_ig))) if event.show_track_proposal: - result.append( (_('Talk Proposals'), '/event/%s/track_proposal/' % slug(event))) + result.append( (_('Talk Proposals'), '/event/%s/track_proposal' % slug(event))) return result # diff --git a/addons/website_event_track/views/website_event.xml b/addons/website_event_track/views/website_event.xml index 03f6f750b6f..3eb2a4bbbde 100644 --- a/addons/website_event_track/views/website_event.xml +++ b/addons/website_event_track/views/website_event.xml @@ -174,7 +174,7 @@