diff --git a/website_mass_mailing_altcha/README.rst b/website_mass_mailing_altcha/README.rst new file mode 100644 index 0000000000..bda62851a7 --- /dev/null +++ b/website_mass_mailing_altcha/README.rst @@ -0,0 +1,86 @@ +===================================== +Privacy Friendly Captcha - Newsletter +===================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:6f73953b7f9de3325c4a5c60a48de2f499e106c8d95e02eb78c4de9ead325a23 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwebsite-lightgray.png?logo=github + :target: https://github.com/OCA/website/tree/18.0/website_mass_mailing_altcha + :alt: OCA/website +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/website-18-0/website-18-0-website_mass_mailing_altcha + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/website&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds ALTCHA support to the newsletter subscription snippets. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +The challenge is solved when the visitor presses **Subscribe**, or +earlier if the visitor ticks the checkbox. Every challenge is accepted +only once and expires after the timeout configured on the website. + +ALTCHA requires a secure context, so the widget does not work over plain +HTTP. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Nitrokey GmbH + +Contributors +------------ + +- Nitrokey GmbH (https://www.nitrokey.com) + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/website `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_mass_mailing_altcha/__init__.py b/website_mass_mailing_altcha/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website_mass_mailing_altcha/__manifest__.py b/website_mass_mailing_altcha/__manifest__.py new file mode 100644 index 0000000000..ad12938370 --- /dev/null +++ b/website_mass_mailing_altcha/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2026 Nitrokey GmbH +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Privacy Friendly Captcha - Newsletter", + "summary": "Support newsletter subscription with ALTCHA", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Nitrokey GmbH,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/website", + "depends": [ + "website_altcha", + "website_mass_mailing", + ], + "assets": { + "web.assets_frontend": [ + "website_mass_mailing_altcha/static/src/*.esm.js", + ], + "web.assets_tests": [ + "website_mass_mailing_altcha/static/tests/tours/*.esm.js", + ], + }, + "demo": [ + "demo/newsletter_page.xml", + ], + "auto_install": True, +} diff --git a/website_mass_mailing_altcha/demo/newsletter_page.xml b/website_mass_mailing_altcha/demo/newsletter_page.xml new file mode 100644 index 0000000000..4e1afd2545 --- /dev/null +++ b/website_mass_mailing_altcha/demo/newsletter_page.xml @@ -0,0 +1,49 @@ + + + + ALTCHA Demo Newsletter + + + + /website_mass_mailing_altcha_demo + + + + diff --git a/website_mass_mailing_altcha/pyproject.toml b/website_mass_mailing_altcha/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/website_mass_mailing_altcha/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/website_mass_mailing_altcha/readme/CONTRIBUTORS.md b/website_mass_mailing_altcha/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..baad82fc41 --- /dev/null +++ b/website_mass_mailing_altcha/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Nitrokey GmbH (https://www.nitrokey.com) diff --git a/website_mass_mailing_altcha/readme/DESCRIPTION.md b/website_mass_mailing_altcha/readme/DESCRIPTION.md new file mode 100644 index 0000000000..03a63a5d7a --- /dev/null +++ b/website_mass_mailing_altcha/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module adds ALTCHA support to the newsletter subscription snippets. diff --git a/website_mass_mailing_altcha/readme/USAGE.md b/website_mass_mailing_altcha/readme/USAGE.md new file mode 100644 index 0000000000..f4dc53aed4 --- /dev/null +++ b/website_mass_mailing_altcha/readme/USAGE.md @@ -0,0 +1,5 @@ +The challenge is solved when the visitor presses **Subscribe**, or earlier if +the visitor ticks the checkbox. Every challenge is accepted only once and +expires after the timeout configured on the website. + +ALTCHA requires a secure context, so the widget does not work over plain HTTP. diff --git a/website_mass_mailing_altcha/static/description/index.html b/website_mass_mailing_altcha/static/description/index.html new file mode 100644 index 0000000000..852f492b72 --- /dev/null +++ b/website_mass_mailing_altcha/static/description/index.html @@ -0,0 +1,432 @@ + + + + + +Privacy Friendly Captcha - Newsletter + + + +
+

Privacy Friendly Captcha - Newsletter

+ + +

Beta License: AGPL-3 OCA/website Translate me on Weblate Try me on Runboat

+

This module adds ALTCHA support to the newsletter subscription snippets.

+

Table of contents

+ +
+

Usage

+

The challenge is solved when the visitor presses Subscribe, or +earlier if the visitor ticks the checkbox. Every challenge is accepted +only once and expires after the timeout configured on the website.

+

ALTCHA requires a secure context, so the widget does not work over plain +HTTP.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Nitrokey GmbH
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/website project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/website_mass_mailing_altcha/static/src/newsletter.esm.js b/website_mass_mailing_altcha/static/src/newsletter.esm.js new file mode 100644 index 0000000000..65a88fee74 --- /dev/null +++ b/website_mass_mailing_altcha/static/src/newsletter.esm.js @@ -0,0 +1,55 @@ +/* Copyright 2026 Nitrokey GmbH */ +import "@website_mass_mailing/js/website_mass_mailing"; +import {AltchaLegacyClassFunctionality} from "@website_altcha/altcha.esm"; +import publicWidget from "@web/legacy/js/public/public_widget"; +import {renderToString} from "@web/core/utils/render"; +import {rpcBus} from "@web/core/network/rpc"; + +const SUBSCRIBE_ROUTE = "/website_mass_mailing/subscribe"; + +publicWidget.registry.subscribe.include({ + ...AltchaLegacyClassFunctionality, + + // Append after the email input, that one must stay the snippet's first + // input, and skip edit mode where the widget would end up being saved + altcha_insert_widget() { + if (this.editableMode || !this.altcha_enabled) { + return; + } + if (!this.el.querySelector("altcha-widget")) { + this.$el + .find(".js_subscribe_wrap") + .append(renderToString("website_altcha.AltchaWidget", {})); + } + }, +}); + +publicWidget.registry.subscribe.include({ + // The subscription is an rpc call, so the payload cannot travel as a form + // field: solve the challenge and inject it into the request's parameters + async _onSubscribeClick() { + // Capture the super method, it is only bound until the first await + const _super = this._super.bind(this); + const widget = this.el.querySelector("altcha-widget"); + if (!widget) { + return _super(...arguments); + } + await this._altchaReady; + const payload = + widget.getState?.() === "verified" + ? widget.querySelector('input[name="altcha"]')?.value + : (await widget.verify?.())?.payload; + // The event is fired synchronously before the request is serialized + const injectPayload = ({detail}) => { + if (detail.url === SUBSCRIBE_ROUTE) { + detail.data.params.altcha = payload; + } + }; + rpcBus.addEventListener("RPC:REQUEST", injectPayload); + try { + return await _super(...arguments); + } finally { + rpcBus.removeEventListener("RPC:REQUEST", injectPayload); + } + }, +}); diff --git a/website_mass_mailing_altcha/static/tests/tours/website_mass_mailing_altcha.esm.js b/website_mass_mailing_altcha/static/tests/tours/website_mass_mailing_altcha.esm.js new file mode 100644 index 0000000000..7d64f468fa --- /dev/null +++ b/website_mass_mailing_altcha/static/tests/tours/website_mass_mailing_altcha.esm.js @@ -0,0 +1,30 @@ +/* Copyright 2026 Nitrokey GmbH */ +import {registry} from "@web/core/registry"; + +registry.category("web_tour.tours").add("website_mass_mailing_altcha", { + steps: () => [ + { + content: "Verify the widget was inserted", + trigger: ".js_subscribe altcha-widget", + }, + { + content: "Fill in the email address", + trigger: ".js_subscribe input.js_subscribe_value", + run: "edit test_website_mass_mailing_altcha@test.com", + }, + { + content: "Subscribe without verifying first", + trigger: ".js_subscribe .js_subscribe_btn", + run: "click", + }, + { + content: "Verify altcha solved the challenge on the fly", + trigger: + ".js_subscribe altcha-widget input[name='altcha']:not(:visible):not(:empty)", + }, + { + content: "Verify the subscription was accepted", + trigger: ".js_subscribe .js_subscribed_wrap:not(.d-none)", + }, + ], +}); diff --git a/website_mass_mailing_altcha/tests/__init__.py b/website_mass_mailing_altcha/tests/__init__.py new file mode 100644 index 0000000000..931da55a4c --- /dev/null +++ b/website_mass_mailing_altcha/tests/__init__.py @@ -0,0 +1 @@ +from . import test_website_mass_mailing_altcha diff --git a/website_mass_mailing_altcha/tests/test_website_mass_mailing_altcha.py b/website_mass_mailing_altcha/tests/test_website_mass_mailing_altcha.py new file mode 100644 index 0000000000..8e5a751db7 --- /dev/null +++ b/website_mass_mailing_altcha/tests/test_website_mass_mailing_altcha.py @@ -0,0 +1,19 @@ +# Copyright 2026 Nitrokey GmbH +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.website_altcha.tests.common import Common + + +class TestWebsiteMassMailingAltcha(Common): + def test_newsletter_subscription(self): + """ + Test subscribing to a newsletter snippet + """ + mailing_list = self.env.ref("website_mass_mailing_altcha.demo_mailing_list") + self.start_tour( + "/website_mass_mailing_altcha_demo", "website_mass_mailing_altcha" + ) + self.assertIn( + "test_website_mass_mailing_altcha@test.com", + mailing_list.contact_ids.mapped("email"), + )