summaryrefslogtreecommitdiff
path: root/addons/website
diff options
context:
space:
mode:
Diffstat (limited to 'addons/website')
-rw-r--r--addons/website/__manifest__.py1
-rw-r--r--addons/website/controllers/main.py1
-rw-r--r--addons/website/models/__init__.py2
-rw-r--r--addons/website/models/ir_http.py66
-rw-r--r--addons/website/models/website.py1
-rw-r--r--addons/website/models/website_page.py2
-rw-r--r--addons/website/models/website_redirect.py18
-rw-r--r--addons/website/models/website_rewrite.py104
-rw-r--r--addons/website/security/ir.model.access.csv5
-rw-r--r--addons/website/static/src/js/menu/content.js2
-rw-r--r--addons/website/tests/test_lang_url.py6
-rw-r--r--addons/website/views/res_config_settings_views.xml7
-rw-r--r--addons/website/views/website_rewrite.xml80
-rw-r--r--addons/website/views/website_views.xml63
14 files changed, 259 insertions, 99 deletions
diff --git a/addons/website/__manifest__.py b/addons/website/__manifest__.py
index 82483b63bd2..3c86cc69a0e 100644
--- a/addons/website/__manifest__.py
+++ b/addons/website/__manifest__.py
@@ -29,6 +29,7 @@
'views/website_views.xml',
'views/website_visitor_views.xml',
'views/res_config_settings_views.xml',
+ 'views/website_rewrite.xml',
'views/ir_actions_views.xml',
'views/ir_attachment_views.xml',
'views/res_partner_views.xml',
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 6ac7ef197f8..e1252e0e23b 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -4,7 +4,6 @@ import base64
import datetime
import json
import os
-import re
import logging
import pytz
import requests
diff --git a/addons/website/models/__init__.py b/addons/website/models/__init__.py
index 1c32e9d514a..83ef426cacf 100644
--- a/addons/website/models/__init__.py
+++ b/addons/website/models/__init__.py
@@ -11,7 +11,7 @@ from . import mixins
from . import website
from . import website_menu
from . import website_page
-from . import website_redirect
+from . import website_rewrite
from . import ir_rule
from . import ir_ui_view
from . import res_company
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index ccfc2c531e4..2ad541d3e33 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -11,6 +11,8 @@ import werkzeug
import werkzeug.routing
import werkzeug.utils
+from functools import partial
+
import odoo
from odoo import api, models, registry
from odoo import SUPERUSER_ID
@@ -62,6 +64,58 @@ class Http(models.AbstractModel):
_inherit = 'ir.http'
@classmethod
+ def routing_map(cls, key=None):
+ key = key or (request and request.website_routing)
+ return super(Http, cls).routing_map(key=key)
+
+ @classmethod
+ def clear_caches(cls):
+ super(Http, cls)._clear_routing_map()
+ return super(Http, cls).clear_caches()
+
+ @classmethod
+ def _slug_matching(cls, adapter, endpoint, **kw):
+ for arg in kw:
+ if isinstance(kw[arg], models.BaseModel):
+ kw[arg] = kw[arg].with_user(request.uid)
+ qs = request.httprequest.query_string.decode('utf-8')
+ return adapter.build(endpoint, kw) + (qs and '?%s' % qs or '')
+
+ @classmethod
+ def _match(cls, path_info, key=None):
+ key = key or (request and request.website_routing)
+ return super(Http, cls)._match(path_info, key=key)
+
+ @classmethod
+ def _generate_routing_rules(cls, modules, converters):
+ website_id = request.website_routing
+ logger.debug("_generate_routing_rules for website: %s", website_id)
+ domain = [('redirect_type', 'in', ('rewrite', 'not_found')), '|', ('website_id', '=', False), ('website_id', '=', website_id)]
+
+ rewrites = dict([(x.url_from, x) for x in request.env['website.rewrite'].sudo().search(domain)])
+ cls._rewrite_len[website_id] = len(rewrites)
+
+ for url, endpoint, routing in super(Http, cls)._generate_routing_rules(modules, converters):
+ routing = dict(routing)
+ if url in rewrites:
+ rewrite = rewrites[url]
+ url_to = rewrite.url_to
+ if rewrite.redirect_type == 'rewrite':
+ logger.debug('Add rule %s for %s' % (url_to, website_id))
+ yield url_to, endpoint, routing # yield new url
+
+ if url != url_to:
+ logger.debug('Redirect from %s to %s for website %s' % (url, url_to, website_id))
+ _slug_matching = partial(cls._slug_matching, endpoint=endpoint)
+ routing['redirect_to'] = _slug_matching
+ yield url, endpoint, routing # yield original redirected to new url
+ elif rewrite.redirect_type == 'not_found':
+ logger.debug('Return 404 for %s for website %s' % (url, website_id))
+ continue
+ else:
+ yield url, endpoint, routing
+
+ @classmethod
def _get_converters(cls):
""" Get the converters list for custom url pattern werkzeug need to
match Rule. This override adds the website ones.
@@ -111,7 +165,11 @@ class Http(models.AbstractModel):
handling the original request, in which we should create the visitor. We ignore every other rerouting requests.
"""
is_rerouting = hasattr(request, 'routing_iteration')
+
+ request.website_routing = request.env['website'].get_current_website().id
+
response = super(Http, cls)._dispatch()
+
if not is_rerouting:
cls._register_website_track(response)
return response
@@ -186,8 +244,12 @@ class Http(models.AbstractModel):
@classmethod
def _serve_redirect(cls):
req_page = request.httprequest.path
- domain = [('url_from', '=', req_page)] + request.website.website_domain()
- return request.env['website.redirect'].search(domain, limit=1)
+ domain = [
+ ('redirect_type', 'in', ('redirect_301', 'redirect_302')),
+ ('url_from', '=', req_page)
+ ]
+ domain += request.website.website_domain()
+ return request.env['website.rewrite'].sudo().search(domain, limit=1)
@classmethod
def _serve_fallback(cls, exception):
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 0ddca6810e2..f8d8d652e72 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -714,6 +714,7 @@ class Website(models.Model):
converters = rule._converters or {}
if query_string and not converters and (query_string not in rule.build([{}], append_unknown=False)[1]):
continue
+
values = [{}]
# converters with a domain are processed after the other ones
convitems = sorted(
diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py
index 5473efeffe6..1d3d72b68a6 100644
--- a/addons/website/models/website_page.py
+++ b/addons/website/models/website_page.py
@@ -122,7 +122,7 @@ class Page(models.Model):
# Create redirect if needed
if data['create_redirect']:
- self.env['website.redirect'].create({
+ self.env['website.rewrite'].create({
'redirect_type': data['redirect_type'],
'url_from': original_url,
'url_to': url,
diff --git a/addons/website/models/website_redirect.py b/addons/website/models/website_redirect.py
deleted file mode 100644
index a0fbda884b0..00000000000
--- a/addons/website/models/website_redirect.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import fields, models
-
-
-class WebsiteRedirect(models.Model):
- _name = "website.redirect"
- _description = "Website Redirect"
- _order = "sequence, id"
- _rec_name = 'url_from'
-
- redirect_type = fields.Selection([('301', 'Moved permanently (301)'), ('302', 'Moved temporarily (302)')], string='Redirection Type', required=True, default="301")
- url_from = fields.Char('Redirect From', required=True)
- url_to = fields.Char('Redirect To', required=True)
- website_id = fields.Many2one('website', 'Website', ondelete='cascade')
- active = fields.Boolean(default=True)
- sequence = fields.Integer()
diff --git a/addons/website/models/website_rewrite.py b/addons/website/models/website_rewrite.py
new file mode 100644
index 00000000000..77acdb8c120
--- /dev/null
+++ b/addons/website/models/website_rewrite.py
@@ -0,0 +1,104 @@
+from odoo import models, fields, api
+from odoo.exceptions import AccessDenied
+
+import logging
+_logger = logging.getLogger(__name__)
+
+
+class WebsiteRoute(models.Model):
+ _rec_name = 'path'
+ _name = 'website.route'
+ _description = "All Website Route"
+ _order = 'path'
+
+ path = fields.Char('Route')
+
+ @api.model
+ def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
+ res = super(WebsiteRoute, self)._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
+ if not len(res):
+ self._refresh()
+ return super(WebsiteRoute, self)._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
+ return res
+
+ def _refresh(self):
+ _logger.debug("Refreshing website.route")
+ ir_http = self.env['ir.http']
+ tocreate = []
+ paths = {rec.path: rec for rec in self.search([])}
+ for url, _, routing in ir_http._generate_routing_rules(self.pool._init_modules, converters=ir_http._get_converters()):
+ if 'GET' in (routing.get('methods') or ['GET']):
+ if paths.get(url):
+ paths.pop(url)
+ else:
+ tocreate.append({'path': url})
+
+ if tocreate:
+ _logger.info("Add %d website.route" % len(tocreate))
+ self.create(tocreate)
+
+ if paths:
+ find = self.search([('path', 'in', list(paths.keys()))])
+ _logger.info("Delete %d website.route" % len(find))
+ find.unlink()
+
+
+class WebsiteRewrite(models.Model):
+ _name = 'website.rewrite'
+ _description = "Website rewrite"
+
+ name = fields.Char('Name', required=True)
+ website_id = fields.Many2one('website', string="Website", ondelete='cascade', index=True)
+ active = fields.Boolean(default=True)
+ url_from = fields.Char('URL from', index=True)
+ route_id = fields.Many2one('website.route')
+ url_to = fields.Char("URL to")
+ redirect_type = fields.Selection([
+ ('not_found', '404 Not Found'),
+ ('redirect_301', '301 Moved permanently'),
+ ('redirect_302', '302 Moved temporarily'),
+ ('rewrite', '308 Redirect / Rewrite'),
+ ], string='Action', default="redirect_302",
+ help='''Type of redirect/Rewrite:\n
+ 301 Moved permanently: The browser will keep in cache the new url.
+ 302 Moved temporarily: The browser will not keep in cache the new url and ask again the next time the new url.
+ 404 Not Found: If you want remove a specific page/controller (e.g. Ecommerce is installed, but you don't want /shop on a specific website)
+ 308 Redirect / Rewrite: If you want rename a controller with a new url. (Eg: /shop -> /garden - Both url will be accessible but /shop will automatically be redirected to /garden)
+ ''')
+
+ sequence = fields.Integer()
+
+ @api.onchange('route_id')
+ def _onchange_route_id(self):
+ self.url_from = self.route_id.path
+ self.url_to = self.route_id.path
+
+ def name_get(self):
+ result = []
+ for rewrite in self:
+ name = rewrite.redirect_type + ' - ' + rewrite.name
+ result.append((rewrite.id, name))
+ return result
+
+ @api.model
+ def create(self, vals):
+ res = super(WebsiteRewrite, self).create(vals)
+ self._invalidate_routing()
+ return res
+
+ def write(self, vals):
+ res = super(WebsiteRewrite, self).write(vals)
+ self._invalidate_routing()
+ return res
+
+ def unlink(self):
+ res = super(WebsiteRewrite, self).unlink()
+ self._invalidate_routing()
+ return res
+
+ def _invalidate_routing(self):
+ # call clear_caches on this worker to reload routing table
+ self.env['ir.http'].clear_caches()
+
+ def refresh_routes(self):
+ self.env['website.route']._refresh()
diff --git a/addons/website/security/ir.model.access.csv b/addons/website/security/ir.model.access.csv
index 624735ca4a4..242c3bd2ea5 100644
--- a/addons/website/security/ir.model.access.csv
+++ b/addons/website/security/ir.model.access.csv
@@ -3,8 +3,8 @@ access_website_public,website,website.model_website,,1,0,0,0
access_website_designer,website,website.model_website,group_website_designer,1,1,1,1
access_website_menu,access_website_menu,model_website_menu,,1,0,0,0
access_website_menu_designer,Web Menu Manager,model_website_menu,group_website_designer,1,1,1,1
-access_website_redirect,access_website_redirect,model_website_redirect,,1,0,0,0
-access_website_redirect_designer,Web Redirect Manager,model_website_redirect,group_website_designer,1,1,1,1
+access_website_rewrite,access_website_rewrite,model_website_rewrite,,0,0,0,0
+access_website_rewrite_designer,Web Rewrite Manager,model_website_rewrite,group_website_designer,1,1,1,1
access_website_page,access_website_page,model_website_page,,1,0,0,0
access_website_page_designer,Web Page Manager,model_website_page,group_website_designer,1,1,1,1
access_website,web menu manager,website.model_website,group_website_designer,1,1,1,1
@@ -18,3 +18,4 @@ access_website_visitor_lastconnection_designer,access_website_visitor_lastconnec
access_website_visitor_lastconnection_system,access_website_visitor_lastconnection_system,model_website_visitor_lastconnection,base.group_system,1,1,1,1
access_website_track_designer,access_website_track_designer,model_website_track,website.group_website_designer,1,1,1,1
access_website_track_system,access_website_track_system,model_website_track,base.group_system,1,1,1,1
+access_website_route_designer,access_website_designer_route,model_website_route,group_website_designer,1,1,1,1 \ No newline at end of file
diff --git a/addons/website/static/src/js/menu/content.js b/addons/website/static/src/js/menu/content.js
index c0daa93925f..6aeeb9d5085 100644
--- a/addons/website/static/src/js/menu/content.js
+++ b/addons/website/static/src/js/menu/content.js
@@ -86,7 +86,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({
}));
defs.push(this._rpc({
- model: 'website.redirect',
+ model: 'website.rewrite',
method: 'fields_get',
}).then(function (fields) {
self.fields = fields;
diff --git a/addons/website/tests/test_lang_url.py b/addons/website/tests/test_lang_url.py
index f4998168074..871248c2778 100644
--- a/addons/website/tests/test_lang_url.py
+++ b/addons/website/tests/test_lang_url.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.addons.http_routing.models.ir_http import url_for
+from odoo.addons.http_routing.models.ir_http import url_lang
from odoo.addons.website.tools import MockRequest
from odoo.tests import HttpCase, tagged
@@ -18,9 +18,9 @@ class TestLangUrl(HttpCase):
self.website.language_ids = self.env.ref('base.lang_en') + lang_fr
self.website.default_lang_id = self.env.ref('base.lang_en')
- def test_01_url_for(self):
+ def test_01_url_lang(self):
with MockRequest(self.env, website=self.website):
- self.assertEqual(url_for('', '[lang]'), '/[lang]/hello/', "`[lang]` is used to be replaced in the url_return after installing a language, it should not be replaced or removed.")
+ self.assertEqual(url_lang('', '[lang]'), '/[lang]/hello/', "`[lang]` is used to be replaced in the url_return after installing a language, it should not be replaced or removed.")
def test_02_url_redirect(self):
url = '/fr_WHATEVER/contactus'
diff --git a/addons/website/views/res_config_settings_views.xml b/addons/website/views/res_config_settings_views.xml
index ff0ac73f2b5..4c3f7e6c296 100644
--- a/addons/website/views/res_config_settings_views.xml
+++ b/addons/website/views/res_config_settings_views.xml
@@ -380,13 +380,6 @@
sequence="30"
/>
- <menuitem name="Redirects"
- id="menu_website_redirect_list"
- action="action_website_redirect_list"
- parent="menu_website_global_configuration"
- sequence="40"
- />
-
<menuitem name="Menus"
id="menu_website_menu_list"
action="action_website_menu"
diff --git a/addons/website/views/website_rewrite.xml b/addons/website/views/website_rewrite.xml
new file mode 100644
index 00000000000..e41111c3bf1
--- /dev/null
+++ b/addons/website/views/website_rewrite.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <!-- Attachment -->
+ <record id="view_website_rewrite_form" model="ir.ui.view">
+ <field name="model">website.rewrite</field>
+ <field name="arch" type="xml">
+ <form string="Website rewrite Settings">
+ <header>
+ <button name="refresh_routes" string="Refresh route's list" type="object"
+ class="btn-primary"
+ attrs="{'invisible':[('redirect_type', '!=', 'rewrite')]}"
+ />
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="name"/>
+ <field name="redirect_type"/>
+ <field name="url_from" attrs="{'invisible': [('redirect_type', '=', 'rewrite')]}"/>
+ <field name="route_id" string="URL from" options="{'no_create': True, 'no_open': True}" attrs="{'invisible': [('redirect_type', '!=', 'rewrite')]}"/>
+ <field name="url_to" attrs="{'invisible': [('redirect_type', '=', 'not_found')]}"/>
+ </group>
+ <group>
+ <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
+ <field name="active"/>
+ <field name="sequence" groups="base.group_no_one"/>
+ </group>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_website_rewrite_tree" model="ir.ui.view">
+ <field name="name">website.rewrite.list</field>
+ <field name="model">website.rewrite</field>
+ <field name="arch" type="xml">
+ <tree string="Website rewrites">
+ <field name="redirect_type"/>
+ <field name="name"/>
+ <field name="url_from"/>
+ <field name="url_to"/>
+ <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
+ <field name="active"/>
+ <field name="sequence" widget="handle" />
+ </tree>
+ </field>
+ </record>
+
+
+ <record id="action_website_rewrite_list" model="ir.actions.act_window">
+ <field name="name">Rewrite</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">website.rewrite</field>
+ <field name="view_id" eval="False"/>
+ </record>
+
+ <menuitem name="Redirects"
+ id="menu_website_rewrite"
+ action="action_website_rewrite_list"
+ parent="menu_website_global_configuration"
+ sequence="30"
+ groups="base.group_no_one"/>
+
+ <record id="view_rewrite_search" model="ir.ui.view">
+ <field name="name">website.rewrite.search</field>
+ <field name="model">website.rewrite</field>
+ <field name="arch" type="xml">
+ <search string="Search Redirect">
+ <field name="url_from"/>
+ <field name="url_to"/>
+ <separator/>
+ <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
+ <group string="Group By">
+ <filter string="Redirection Type" name="group_by_type" domain="[]" context="{'group_by': 'redirect_type'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/website/views/website_views.xml b/addons/website/views/website_views.xml
index 92841cb2063..cac1ba89b28 100644
--- a/addons/website/views/website_views.xml
+++ b/addons/website/views/website_views.xml
@@ -62,23 +62,6 @@
</field>
</record>
-
- <record id="view_redirect_search" model="ir.ui.view">
- <field name="name">website.redirect.search</field>
- <field name="model">website.redirect</field>
- <field name="arch" type="xml">
- <search string="Search Redirect">
- <field name="url_from"/>
- <field name="url_to"/>
- <separator/>
- <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
- <group string="Group By">
- <filter string="Redirection Type" name="group_by_type" domain="[]" context="{'group_by': 'redirect_type'}"/>
- </group>
- </search>
- </field>
- </record>
-
<record id="action_website_list" model="ir.actions.act_window">
<field name="name">Websites</field>
<field name="res_model">website</field>
@@ -87,52 +70,6 @@
<field name="target">current</field>
</record>
- <!-- website.redirect views -->
- <record id="website_redirect_form_view" model="ir.ui.view">
- <field name="name">website.redirect.form</field>
- <field name="model">website.redirect</field>
- <field name="arch" type="xml">
- <form string="Website Redirect Settings">
- <sheet>
- <group>
- <group>
- <field name="redirect_type"/>
- <field name="url_from"/>
- <field name="url_to"/>
- </group>
- <group>
- <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
- <field name="active"/>
- </group>
- </group>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="website_redirect_tree_view" model="ir.ui.view">
- <field name="name">website.redirect.list</field>
- <field name="model">website.redirect</field>
- <field name="arch" type="xml">
- <tree string="Website Redirects" editable="bottom">
- <field name="redirect_type"/>
- <field name="url_from"/>
- <field name="url_to"/>
- <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
- <field name="active"/>
- <field name="sequence" widget="handle" />
- </tree>
- </field>
- </record>
-
- <record id="action_website_redirect_list" model="ir.actions.act_window">
- <field name="name">Website Redirects</field>
- <field name="res_model">website.redirect</field>
- <field name="view_mode">tree,form</field>
- <field name="view_id" ref="website_redirect_tree_view"/>
- <field name="target">current</field>
- <field name="search_view_id" ref="view_redirect_search"/>
- </record>
<!-- website.page views -->
<record id="website_pages_form_view" model="ir.ui.view">