Skip to content

Commit

Permalink
[ADD] website_url_sitemap_exclusion
Browse files Browse the repository at this point in the history
  • Loading branch information
SirTakobi committed Jul 26, 2023
1 parent 09f81ed commit 4d86048
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 0 deletions.
6 changes: 6 additions & 0 deletions setup/website_url_sitemap_exclusion/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
3 changes: 3 additions & 0 deletions website_url_sitemap_exclusion/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import models
21 changes: 21 additions & 0 deletions website_url_sitemap_exclusion/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2023 Simone Rubino - TAKOBI
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Website URL Exclusion",
"summary": "Exclude URLs from the website sitemap",
"version": "12.0.1.0.0",
"category": "Website",
"license": "AGPL-3",
"website": "https://github.com/OCA/website"
"/tree/12.0/website_url_sitemap_exclusion",
"author": "TAKOBI, "
"Odoo Community Association (OCA)",
"depends": [
"website",
],
"data": [
"security/ir.model.access.csv",
"views/website_views.xml",
],
}
80 changes: 80 additions & 0 deletions website_url_sitemap_exclusion/i18n/it.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * website_url_sitemap_exclusion
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-27 13:13+0000\n"
"PO-Revision-Date: 2023-02-27 13:13+0000\n"
"Last-Translator: Simone Rubino <[email protected]>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__create_uid
msgid "Created by"
msgstr "Creato da"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__create_date
msgid "Created on"
msgstr "Creato il"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__display_name
msgid "Display Name"
msgstr "Nome visualizzato"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website__forbidden_url_ids
msgid "Forbidden URLs regex"
msgstr "Espressioni regolari per URL vietati"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__id
msgid "ID"
msgstr ""

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"

#. module: website_url_sitemap_exclusion
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__regex
msgid "Regex"
msgstr "Espressione regolare"

#. module: website_url_sitemap_exclusion
#: code:addons/website_url_sitemap_exclusion/models/website_forbidden_url.py:35
#, python-format
msgid "Regular Expression `{regex}` is not valid:\n"
"{error}"
msgstr "L'espressione regolare `{regex}` non è valida:\n"
"{error}"

#. module: website_url_sitemap_exclusion
#: model:ir.model,name:website_url_sitemap_exclusion.model_website_forbidden_url
msgid "URL not allowed in the sitemap"
msgstr "URL non consentito nella sitemap"

#. module: website_url_sitemap_exclusion
#: model:ir.model,name:website_url_sitemap_exclusion.model_website
#: model:ir.model.fields,field_description:website_url_sitemap_exclusion.field_website_forbidden_url__website_id
msgid "Website"
msgstr "Sito web"
4 changes: 4 additions & 0 deletions website_url_sitemap_exclusion/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import website
from . import website_forbidden_url
39 changes: 39 additions & 0 deletions website_url_sitemap_exclusion/models/website.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2023 Simone Rubino - TAKOBI
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import re
from odoo import api, fields, models


class Website(models.Model):
_inherit = 'website'

forbidden_url_ids = fields.One2many(
comodel_name="website.forbidden.url",
inverse_name="website_id",
string="Forbidden URLs regex",
)

@api.multi
def enumerate_pages(self, query_string=None, force=False):
pages = super().enumerate_pages(query_string=query_string, force=force)
forbidden_url_regexes = self.mapped("forbidden_url_ids.regex")
forbidden_url_compiled_regexes = list(map(re.compile, forbidden_url_regexes))
for page in pages:
is_forbidden = \
self._is_enumerated_page_forbidden_by_regexes(
page, forbidden_url_compiled_regexes,
)
if not is_forbidden:
yield page

def _is_enumerated_page_forbidden_by_regexes(self, page, compiled_regexes):
page_url = page["loc"]
for forbidden_url_compiled_regex in compiled_regexes:
is_url_forbidden = forbidden_url_compiled_regex.match(page_url)
if is_url_forbidden:
is_forbidden = True
break
else:
is_forbidden = False
return is_forbidden
41 changes: 41 additions & 0 deletions website_url_sitemap_exclusion/models/website_forbidden_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2023 Simone Rubino - TAKOBI
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import re

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError


class WebsiteForbiddenUrls(models.Model):
_name = "website.forbidden.url"
_description = "URL not allowed in the sitemap"
_rec_name = 'regex'

regex = fields.Char(
string="Regular Expression",
required=True,
)
website_id = fields.Many2one(
comodel_name="website",
ondelete="cascade",
)

@api.constrains(
'regex',
)
def constrain_regex_valid(self):
"""RegEx must be valid."""
for forbidden_url in self:
regex = forbidden_url.regex
try:
re.compile(regex)
except re.error as error:
raise ValidationError(
_("Regular Expression `{regex}` is not valid:\n"
"{error}")
.format(
regex=regex,
error=error.msg,
)
) from error
1 change: 1 addition & 0 deletions website_url_sitemap_exclusion/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In the Website form, add Regular Expressions to the list `Forbidden URLs regex`.
6 changes: 6 additions & 0 deletions website_url_sitemap_exclusion/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Allow to exclude URLs from the sitemap.

The URLs can be selected using Regular Expressions.

This module can be used to exclude URLs that cannot be excluded using page properties;
for instance, this can be used to exclude the URL `/shop` if there are no published products.
3 changes: 3 additions & 0 deletions website_url_sitemap_exclusion/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_website_forbidden_url_all,Everyone can read forbidden URLs,model_website_forbidden_url,,1,0,0,0
manage_website_forbidden_url_designer,Designers can manage forbidden URLs,model_website_forbidden_url,website.group_website_designer,1,1,1,1
3 changes: 3 additions & 0 deletions website_url_sitemap_exclusion/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import test_sitemap_exclusion
80 changes: 80 additions & 0 deletions website_url_sitemap_exclusion/tests/test_sitemap_exclusion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright 2023 Simone Rubino - TAKOBI
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import datetime
from unittest.mock import patch

from odoo import tests
from odoo.addons.website.controllers.main import Website
from odoo.addons.website.tools import MockRequest
from odoo.exceptions import ValidationError
from odoo.tests import Form


class TestSiteMapExclusion (tests.SavepointCase):

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.controller = Website()
cls.website = cls.env['website'].browse(1)
cls.sitemap = cls._get_sitemap(cls.env)

@classmethod
def _get_sitemap(cls, env):
"""Get the updated sitemap.
Usually the sitemap is cached for 12 hours,
here we reset the cache so that the sitemap returned is always updated.
"""
base_url = cls.env['ir.config_parameter'].sudo().get_param('web.base.url')

sitemap_cache_duration_path = 'odoo.addons.website' \
'.controllers.main.SITEMAP_CACHE_TIME'
no_cache = datetime.timedelta(seconds=-1)
with patch(sitemap_cache_duration_path, new=no_cache), \
MockRequest(env, website=cls.website) as request:
request.httprequest.url_root = base_url
cls.controller.sitemap_xml_index()
# Sitemap content is the first parameter
# of the first call to `make_response`
sitemap = request.make_response.call_args[0][0]
return sitemap

def test_url_exclusion(self):
"""URLs defined in the Website are excluded from the sitemap."""
# Arrange: We want to exclude a URL
url_to_exclude = '/aboutus'
url_regex = '/aboutus'
sitemap = self.sitemap
# pre-condition: The URL is in the sitemap,
# and the regex matches the URL
self.assertRegex(url_to_exclude, url_regex)
self.assertIn(url_to_exclude, sitemap.decode())

# Act: Exclude the URL in the website
website_form = Form(self.website)
with website_form.forbidden_url_ids.new() as forbid:
forbid.regex = url_regex
website_form.save()

# Assert: The sitemap does not contain the URL
new_sitemap = TestSiteMapExclusion._get_sitemap(self.env)
self.assertNotIn(url_to_exclude, new_sitemap.decode())

def test_not_valid_regex(self):
"""Only valid Regular Expressions can be used."""
# Arrange: A Regular Expression is not valid
not_valid_regex = '/abo[utus'

# Act: Add the Regular Expression to the website forbidden URLs
website_form = Form(self.website)
with website_form.forbidden_url_ids.new() as forbid:
forbid.regex = not_valid_regex
with self.assertRaises(ValidationError) as ve:
website_form.save()
exc_message = ve.exception.args[0]

# Assert: The validation error mentions the wrong Regular Expression
self.assertIn(not_valid_regex, exc_message)
self.assertIn('not valid', exc_message)
24 changes: 24 additions & 0 deletions website_url_sitemap_exclusion/views/website_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2023 Simone Rubino - TAKOBI
~ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->

<odoo>
<record id="view_website_form" model="ir.ui.view">
<field name="name">Add Forbidden URLs to Website Form View</field>
<field name="model">website</field>
<field name="inherit_id" ref="website.view_website_form"/>
<field name="arch" type="xml">
<div name="other" position="after">
<group name="forbidden_urls">
<field name="forbidden_url_ids">
<tree editable="top">
<field name="regex"/>
</tree>
</field>
</group>
</div>
</field>
</record>
</odoo>

0 comments on commit 4d86048

Please sign in to comment.