[IMP] base, http_routing, website: allow custom routing rule

After this commit, you will be able (in technical mode) to update the url for
the python controllers.

Eg.
You can now rename /shop in /garden and /shop/product/ in /garden/vegetable/

Most of urls will be replaced at fly in the renderd qweb, with the function
url_for but all old urls will keep available. So if you access url /shop you
will be automatically redirected to /garden (308 Permanent Redirect).

As for cdn and other post-process of att, the automatically replacement in the
rendered qweb is only done when you will be not website editor. But the new
dispatch of URL will be applied in all cases.

For developper, since it is Permanent Redirect, don't forget to clear cache or
open chrome debug tool (with option 'Disable cache while DevTools is Open) to
see your lasts changes.

closes odoo/odoo#36555

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
Jeremy Kersten
2019-09-30 13:58:14 +00:00
parent b6b42ef73f
commit be8fc2296b
17 changed files with 349 additions and 143 deletions
+27 -12
View File
@@ -87,8 +87,12 @@ class IrHttp(models.AbstractModel):
return {'model': ModelConverter, 'models': ModelsConverter, 'int': SignedIntConverter}
@classmethod
def _find_handler(cls, return_rule=False):
return cls.routing_map().bind_to_environ(request.httprequest.environ).match(return_rule=return_rule)
def _match(cls, path_info, key=None):
return cls.routing_map(key=key).bind('').match(
return_rule=True,
method=request.httprequest.method,
path_info=path_info
)
@classmethod
def _auth_method_user(cls):
@@ -208,7 +212,7 @@ class IrHttp(models.AbstractModel):
# locate the controller method
try:
rule, arguments = cls._find_handler(return_rule=True)
rule, arguments = cls._match(request.httprequest.path)
func = rule.endpoint
except werkzeug.exceptions.NotFound as e:
return cls._handle_exception(e)
@@ -245,24 +249,37 @@ class IrHttp(models.AbstractModel):
return cls._handle_exception(werkzeug.exceptions.NotFound())
@classmethod
def routing_map(cls):
def _generate_routing_rules(cls, modules, converters):
return http._generate_routing_rules(modules, False, converters)
@classmethod
def routing_map(cls, key=None):
if not hasattr(cls, '_routing_map'):
_logger.info("Generating routing map")
installed = request.registry._init_modules - {'web'}
cls._routing_map = {}
cls._rewrite_len = {}
if key not in cls._routing_map:
_logger.info("Generating routing map for key %s" % str(key))
installed = request.registry._init_modules | set(odoo.conf.server_wide_modules)
if tools.config['test_enable'] and odoo.modules.module.current_test:
installed.add(odoo.modules.module.current_test)
mods = [''] + odoo.conf.server_wide_modules + sorted(installed)
mods = sorted(installed)
# Note : when routing map is generated, we put it on the class `cls`
# to make it available for all instance. Since `env` create an new instance
# of the model, each instance will regenared its own routing map and thus
# regenerate its EndPoint. The routing map should be static.
cls._routing_map = http.routing_map(mods, False, converters=cls._get_converters())
return cls._routing_map
routing_map = werkzeug.routing.Map(strict_slashes=False, converters=cls._get_converters())
for url, endpoint, routing in cls._generate_routing_rules(mods, converters=cls._get_converters()):
xtra_keys = 'defaults subdomain build_only strict_slashes redirect_to alias host'.split()
kw = {k: routing[k] for k in xtra_keys if k in routing}
routing_map.add(werkzeug.routing.Rule(url, endpoint=endpoint, methods=routing['methods'], **kw))
cls._routing_map[key] = routing_map
return cls._routing_map[key]
@classmethod
def _clear_routing_map(cls):
if hasattr(cls, '_routing_map'):
del cls._routing_map
cls._routing_map = {}
_logger.debug("Clear routing map")
#------------------------------------------------------
# Binary server
@@ -337,8 +354,6 @@ class IrHttp(models.AbstractModel):
status = 301
content = record.url
return status, content, filename, mimetype, filehash
def _binary_record_content(
+6 -12
View File
@@ -908,9 +908,8 @@ class EndPoint(object):
def __call__(self, *args, **kw):
return self.method(*args, **kw)
def routing_map(modules, nodb_only, converters=None):
routing_map = werkzeug.routing.Map(strict_slashes=False, converters=converters)
def _generate_routing_rules(modules, nodb_only, converters=None):
def get_subclasses(klass):
def valid(c):
return c.__module__.startswith('odoo.addons.') and c.__module__.split(".")[2] in modules
@@ -949,16 +948,8 @@ def routing_map(modules, nodb_only, converters=None):
assert routing['routes'], "Method %r has not route defined" % mv
endpoint = EndPoint(mv, routing)
for url in routing['routes']:
if routing.get("combine", False):
# deprecated v7 declaration
url = o._cp_path.rstrip('/') + '/' + url.lstrip('/')
if url.endswith("/") and len(url) > 1:
url = url[: -1]
yield (url, endpoint, routing)
xtra_keys = 'defaults subdomain build_only strict_slashes redirect_to alias host'.split()
kw = {k: routing[k] for k in xtra_keys if k in routing}
routing_map.add(werkzeug.routing.Rule(url, endpoint=endpoint, methods=routing['methods'], **kw))
return routing_map
#----------------------------------------------------------
# HTTP Sessions
@@ -1276,7 +1267,10 @@ class Root(object):
@lazy_property
def nodb_routing_map(self):
_logger.info("Generating nondb routing")
return routing_map([''] + odoo.conf.server_wide_modules, True)
routing_map = werkzeug.routing.Map(strict_slashes=False, converters=None)
for url, endpoint, routing in odoo.http._generate_routing_rules([''] + odoo.conf.server_wide_modules, True):
routing_map.add(werkzeug.routing.Rule(url, endpoint=endpoint, methods=routing['methods']))
return routing_map
def __call__(self, environ, start_response):
""" Handle a WSGI request