diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py
index a6c93e2939d..33e20f8a76a 100644
--- a/addons/hw_proxy/controllers/main.py
+++ b/addons/hw_proxy/controllers/main.py
@@ -101,7 +101,7 @@ class Proxy(http.Controller):
The list of connected USB devices as seen by the IoTBox
"""
if debug is None:
- resp += """(debug version)"""
+ resp += """(debug version)"""
devices = subprocess.check_output("lsusb").decode('utf-8').split('\n')
count = 0
resp += "\n"
diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py
index 79ec844fdf8..0fca1369384 100644
--- a/addons/point_of_sale/controllers/main.py
+++ b/addons/point_of_sale/controllers/main.py
@@ -12,7 +12,7 @@ _logger = logging.getLogger(__name__)
class PosController(http.Controller):
@http.route('/pos/web', type='http', auth='user')
- def pos_web(self, debug=False, **k):
+ def pos_web(self, **k):
# if user not logged in, log him in
pos_sessions = request.env['pos.session'].search([
('state', '=', 'opened'),
diff --git a/addons/point_of_sale/views/pos_templates.xml b/addons/point_of_sale/views/pos_templates.xml
index dfadf1af349..cb1a8a81a01 100644
--- a/addons/point_of_sale/views/pos_templates.xml
+++ b/addons/point_of_sale/views/pos_templates.xml
@@ -31,12 +31,14 @@
var odoo = {
csrf_token: "",
session_info: ,
+ debug: "",
};
+
@@ -422,6 +431,7 @@
+
@@ -840,6 +850,7 @@
+
diff --git a/addons/web_settings_dashboard/controllers/main.py b/addons/web_settings_dashboard/controllers/main.py
index 574f9e8fd21..fb1105ba939 100644
--- a/addons/web_settings_dashboard/controllers/main.py
+++ b/addons/web_settings_dashboard/controllers/main.py
@@ -73,7 +73,7 @@ class WebSettingsDashboard(http.Controller):
'share': {
'server_version': release.version,
'expiration_date': expiration_date,
- 'debug': request.debug,
+ 'debug': request.session.debug,
'demo_active': demo_active,
},
'company': {
diff --git a/addons/web_settings_dashboard/static/src/xml/dashboard.xml b/addons/web_settings_dashboard/static/src/xml/dashboard.xml
index b8c8f339ebd..5e55e8d7a87 100644
--- a/addons/web_settings_dashboard/static/src/xml/dashboard.xml
+++ b/addons/web_settings_dashboard/static/src/xml/dashboard.xml
@@ -123,13 +123,13 @@
- Activate the developer mode
-
- Activate the developer mode (with assets)
-
- Deactivate the developer mode
-
- Load demo data
+
diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py
index ea33aa114b2..664838afe77 100644
--- a/addons/website/models/ir_qweb.py
+++ b/addons/website/models/ir_qweb.py
@@ -44,7 +44,7 @@ class QWeb(models.AbstractModel):
atts = super(QWeb, self)._post_processing_att(tagName, atts, options)
if options.get('inherit_branding') or options.get('rendering_bundle') or \
- options.get('edit_translations') or options.get('debug') or (request and request.debug):
+ options.get('edit_translations') or options.get('debug') or (request and request.session.debug):
return atts
website = request and getattr(request, 'website', None)
diff --git a/addons/website/tests/test_qweb.py b/addons/website/tests/test_qweb.py
index e3070d9f009..5ca9344aa32 100644
--- a/addons/website/tests/test_qweb.py
+++ b/addons/website/tests/test_qweb.py
@@ -93,7 +93,10 @@ class MockRequest(object):
if not routing:
app.match = werkzeugRaiseNotFound
self.request = MockObject(
- env=env, context=context or {}, db=None, debug=False,
+ env=env, context=context or {}, db=None,
+ session=MockObject(
+ debug=False
+ ),
website=website, httprequest=MockObject(
path='/hello/',
app=app
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index c6f112d554d..3c5904dd034 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -904,7 +904,7 @@
:
-
+
@@ -969,7 +969,7 @@
Contact Us
-
+
@@ -996,7 +996,7 @@
-
+
@@ -1123,7 +1123,7 @@
-
+
diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py
index faf8a3cb8fd..04e85944c0e 100644
--- a/odoo/addons/base/models/assetsbundle.py
+++ b/odoo/addons/base/models/assetsbundle.py
@@ -135,7 +135,7 @@ class AssetsBundle(object):
:returns [(tagName, attributes, content)] if the tag is auto close
"""
response = []
- if debug == 'assets':
+ if debug and 'assets' in debug:
if css and self.stylesheets:
is_css_preprocessed, old_attachments = self.is_css_preprocessed()
if not is_css_preprocessed:
diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py
index 420ba00026f..ac6ccbc9eac 100644
--- a/odoo/addons/base/models/ir_http.py
+++ b/odoo/addons/base/models/ir_http.py
@@ -24,6 +24,9 @@ from odoo.tools import pycompat, consteq
from odoo.tools.mimetypes import guess_mimetype
from odoo.modules.module import get_resource_path, get_module_path
+from odoo.http import ALLOWED_DEBUG_MODES
+from odoo.tools.misc import str2bool
+
_logger = logging.getLogger(__name__)
@@ -125,6 +128,21 @@ class IrHttp(models.AbstractModel):
raise AccessDenied()
return auth_method
+ @classmethod
+ def _handle_debug(cls):
+ # Store URL debug mode (might be empty) into session
+ if 'debug' in request.httprequest.args:
+ debug_mode = []
+ for debug in request.httprequest.args['debug'].split(','):
+ if debug not in ALLOWED_DEBUG_MODES:
+ debug = '1' if str2bool(debug, debug) else ''
+ debug_mode.append(debug)
+ debug_mode = ','.join(debug_mode)
+
+ # Write on session only when needed
+ if debug_mode != request.session.debug:
+ request.session.debug = debug_mode
+
@classmethod
def _serve_attachment(cls):
env = api.Environment(request.cr, SUPERUSER_ID, request.context)
@@ -162,6 +180,9 @@ class IrHttp(models.AbstractModel):
@classmethod
def _handle_exception(cls, exception):
+ # in case of Exception, e.g. 404, we don't step into _dispatch
+ cls._handle_debug()
+
# If handle_exception returns something different than None, it will be used as a response
# This is done first as the attachment path may
@@ -182,6 +203,8 @@ class IrHttp(models.AbstractModel):
@classmethod
def _dispatch(cls):
+ cls._handle_debug()
+
# locate the controller method
try:
rule, arguments = cls._find_handler(return_rule=True)
diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py
index 7459dd29504..d726ee2cfa1 100644
--- a/odoo/addons/base/models/ir_ui_menu.py
+++ b/odoo/addons/base/models/ir_ui_menu.py
@@ -122,7 +122,7 @@ class IrUiMenu(models.Model):
the menu hierarchy of the current user.
Uses a cache for speeding up the computation.
"""
- visible_ids = self._visible_menu_ids(request.debug if request else False)
+ visible_ids = self._visible_menu_ids(request.session.debug if request else False)
return self.filtered(lambda menu: menu.id in visible_ids)
@api.model
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index 8c5f4809f32..30600ee6bf7 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -1356,7 +1356,7 @@ actual arch.
res_company=self.env.company.sudo(),
keep_query=keep_query,
request=request, # might be unbound if we're not in an httprequest context
- debug=request.debug if request else False,
+ debug=request.session.debug if request else '',
json=json,
quote_plus=werkzeug.url_quote_plus,
time=time,
diff --git a/odoo/http.py b/odoo/http.py
index 67243cf5a52..3f4dd6fad27 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -64,6 +64,21 @@ STATIC_CACHE = 60 * 60 * 24 * 7
# To remove when corrected in Babel
babel.core.LOCALE_ALIASES['nb'] = 'nb_NO'
+""" Debug mode is stored in session and should always be a string.
+ It can be activated with an URL query string `debug=` where
+ mode is either:
+ - 'tests' to load tests assets
+ - 'assets' to load assets non minified
+ - any other truthy value to enable simple debug mode (to show some
+ technical feature, to show complete traceback in frontend error..)
+ - any falsy value to disable debug mode
+
+ You can use any truthy/falsy value from `str2bool` (eg: 'on', 'f'..)
+ Multiple debug modes can be activated simultaneously, separated with
+ a comma (eg: 'tests, assets').
+"""
+ALLOWED_DEBUG_MODES = ['', '1', 'assets', 'tests']
+
#----------------------------------------------------------
# RequestHandler
#----------------------------------------------------------
@@ -143,15 +158,10 @@ def dispatch_rpc(service_name, method, params):
odoo.tools.debugger.post_mortem(odoo.tools.config, sys.exc_info())
raise
-def local_redirect(path, query=None, keep_hash=False, forward_debug=True, code=303):
+def local_redirect(path, query=None, keep_hash=False, code=303):
url = path
if not query:
query = {}
- if request and request.debug:
- if forward_debug:
- query['debug'] = ''
- else:
- query['debug'] = None
if query:
url += '?' + werkzeug.url_encode(query)
if keep_hash:
@@ -345,22 +355,6 @@ class WebRequest(object):
return checked_call(self.db, *args, **kwargs)
return self.endpoint(*args, **kwargs)
- @property
- def debug(self):
- """ Indicates whether the current request is in "debug" mode
- """
- debug = 'debug' in self.httprequest.args
- if debug and self.httprequest.args.get('debug') == 'assets':
- debug = 'assets'
-
- # check if request from rpc in debug mode
- if not debug:
- debug = self.httprequest.environ.get('HTTP_X_DEBUG_MODE')
-
- if not debug and self.httprequest.referrer:
- debug = 'debug' in urls.url_parse(self.httprequest.referrer).decode_query()
- return debug
-
@contextlib.contextmanager
def registry_cr(self):
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
@@ -763,7 +757,7 @@ class HttpRequest(WebRequest):
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
headers = {
'Access-Control-Max-Age': 60 * 60 * 24,
- 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode'
+ 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
}
return Response(status=200, headers=headers)
@@ -1038,7 +1032,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
def logout(self, keep_db=False):
for k in list(self):
- if not (keep_db and k == 'db'):
+ if not (keep_db and k == 'db') and k != 'debug':
del self[k]
self._default_values()
self.rotate = True
@@ -1049,6 +1043,9 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.setdefault("login", None)
self.setdefault("session_token", None)
self.setdefault("context", {})
+ # Force tests debug mode when test mode enabled to load 'assets_tests'
+ test_mode = odoo.tools.config['test_enable'] or odoo.tools.config['test_file']
+ self.setdefault("debug", 'tests' if test_mode else '')
def get_context(self):
"""
@@ -1242,13 +1239,12 @@ class Response(werkzeug.wrappers.Response):
class DisableCacheMiddleware(object):
def __init__(self, app):
self.app = app
+
def __call__(self, environ, start_response):
def start_wrapped(status, headers):
- referer = environ.get('HTTP_REFERER', '')
- parsed = urls.url_parse(referer)
- debug = parsed.query.count('debug') >= 1
-
- if debug:
+ req = werkzeug.wrappers.Request(environ)
+ root.setup_session(req)
+ if req.session and req.session.debug:
new_headers = [('Cache-Control', 'no-cache')]
for k, v in headers:
diff --git a/odoo/models.py b/odoo/models.py
index a2499c6a68d..77701cf676a 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -1237,7 +1237,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
for group_ext_id in not_has_groups:
if group_ext_id == 'base.group_no_one':
# check: the group_no_one is effective in debug mode only
- if user.has_group(group_ext_id) and request and request.debug:
+ if user.has_group(group_ext_id) and request and request.session.debug:
return False
else:
if user.has_group(group_ext_id):
@@ -1246,7 +1246,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
for group_ext_id in has_groups:
if group_ext_id == 'base.group_no_one':
# check: the group_no_one is effective in debug mode only
- if user.has_group(group_ext_id) and request and request.debug:
+ if user.has_group(group_ext_id) and request and request.session.debug:
return True
else:
if user.has_group(group_ext_id):