diff --git a/website_altcha/README.rst b/website_altcha/README.rst index e5d69cca7e..6d559a3429 100644 --- a/website_altcha/README.rst +++ b/website_altcha/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ======================== Privacy Friendly Captcha ======================== @@ -17,7 +13,7 @@ Privacy Friendly Captcha .. |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/license-AGPL--3-blue.png +.. |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 @@ -126,6 +122,14 @@ 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. +.. |maintainer-hbrunn| image:: https://github.com/hbrunn.png?size=40px + :target: https://github.com/hbrunn + :alt: hbrunn + +Current `maintainer `__: + +|maintainer-hbrunn| + 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_altcha/__manifest__.py b/website_altcha/__manifest__.py index 2f506e0d84..8c0f918e92 100644 --- a/website_altcha/__manifest__.py +++ b/website_altcha/__manifest__.py @@ -8,21 +8,25 @@ "version": "18.0.1.0.0", "license": "AGPL-3", "author": "Dixmit,Odoo Community Association (OCA)", + "maintainers": ["hbrunn"], "website": "https://github.com/OCA/website", "depends": [ "website", ], "assets": { "web.assets_frontend": [ - "website_altcha/static/src/**/*.esm.js", - "website_altcha/static/src/**/*.xml", - "website_altcha/static/src/**/*.scss", + "website_altcha/static/src/*.esm.js", + "website_altcha/static/src/*.xml", + "website_altcha/static/src/*.scss", ], "web.altcha_libs": [ "website_altcha/static/lib/altcha.js", "website_altcha/static/lib/altcha-l10n.js", "website_altcha/static/lib/altcha.css", ], + "web.assets_tests": [ + "website_altcha/static/tests/tours/*.esm.js", + ], }, "external_dependencies": { "python": ["altcha"], @@ -31,4 +35,7 @@ "security/ir.model.access.csv", "views/res_config_settings_views.xml", ], + "demo": [ + "demo/website_page.xml", + ], } diff --git a/website_altcha/demo/website_page.xml b/website_altcha/demo/website_page.xml new file mode 100644 index 0000000000..ad2f5d5e13 --- /dev/null +++ b/website_altcha/demo/website_page.xml @@ -0,0 +1,36 @@ + + + + /website_altcha_demo + + + + diff --git a/website_altcha/static/description/index.html b/website_altcha/static/description/index.html index 961c70b429..5e079092ee 100644 --- a/website_altcha/static/description/index.html +++ b/website_altcha/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Privacy Friendly Captcha -
+
+

Privacy Friendly Captcha

- - -Odoo Community Association - -
-

Privacy Friendly Captcha

-

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

+

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

This module allows to use a Captcha System completly handled by Odoo.

It relies on Altcha (https://altcha.org), an OpenSource captcha alternative.

@@ -393,7 +388,7 @@

Privacy Friendly Captcha

-

Use Cases / Context

+

Use Cases / Context

Currently, Odoo provides 2 options:

  • Google Recaptcha relies on tracking of the user. It implies cookies
  • @@ -407,7 +402,7 @@

    Use Cases / Context

    to spend more CPU, making it costly at scale.

-

Configuration

+

Configuration

Go to Configuration/Website, check “Enable Altcha” under “Privacy”. Some extra parameters will appear with all the ALTCHA information. This parameters are website dependent.

@@ -436,7 +431,7 @@

Configuration

-

Bug Tracker

+

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 @@ -444,15 +439,15 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Dixmit
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -470,11 +465,12 @@

Maintainers

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.

+

Current maintainer:

+

hbrunn

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_altcha/static/lib/altcha.js b/website_altcha/static/lib/altcha.js index 1b70b870b7..7dab37aa64 100644 --- a/website_altcha/static/lib/altcha.js +++ b/website_altcha/static/lib/altcha.js @@ -1,4 +1,3 @@ -/* @odoo-module */ const DEV = false; var is_array = Array.isArray; var index_of = Array.prototype.indexOf; diff --git a/website_altcha/static/src/altcha.esm.js b/website_altcha/static/src/altcha.esm.js new file mode 100644 index 0000000000..996bfbb97a --- /dev/null +++ b/website_altcha/static/src/altcha.esm.js @@ -0,0 +1,49 @@ +import {loadBundle} from "@web/core/assets"; +import {renderToString} from "@web/core/utils/render"; +import {session} from "@web/session"; + +export const AltchaBaseFunctionality = { + altcha_prepend_to: "div.oe_login_buttons", + altcha_enabled: false, + + altcha_init() { + this.altcha_enabled = Boolean(session.altcha_public_key); + }, + + /** + * Loads the altcha libraries. + * + * @returns {Promise|Boolean} promise if libs are loading else false if the altcha key is empty. + */ + async altcha_load_libs() { + if (this.altcha_enabled) { + this._altchaReady = loadBundle(`web.altcha_libs`); + return this._altchaReady; + } + return false; + }, + altcha_insert_widget() { + if (this.altcha_enabled && !this.$el.find("altcha-widget").length) { + this.$el + .find(this.altcha_prepend_to) + .prepend(renderToString("website_altcha.AltchaWidget", {})); + } + }, +}; + +export const AltchaLegacyClassFunctionality = { + ...AltchaBaseFunctionality, + init() { + this._super(...arguments); + this.altcha_init(); + }, + + async willStart() { + this.altcha_load_libs(); + return this._super(...arguments); + }, + start() { + this.altcha_insert_widget(); + return this._super(...arguments); + }, +}; diff --git a/website_altcha/static/src/signup/altcha.scss b/website_altcha/static/src/altcha.scss similarity index 100% rename from website_altcha/static/src/signup/altcha.scss rename to website_altcha/static/src/altcha.scss diff --git a/website_altcha/static/src/signup/altcha.xml b/website_altcha/static/src/altcha.xml similarity index 100% rename from website_altcha/static/src/signup/altcha.xml rename to website_altcha/static/src/altcha.xml diff --git a/website_altcha/static/src/altcha/altcha.esm.js b/website_altcha/static/src/altcha/altcha.esm.js deleted file mode 100644 index 4f18e82e33..0000000000 --- a/website_altcha/static/src/altcha/altcha.esm.js +++ /dev/null @@ -1,27 +0,0 @@ -/* global document */ - -import {loadBundle} from "@web/core/assets"; -import {session} from "@web/session"; - -export class Altcha { - /** - * @override - */ - constructor() { - this._publicKey = session.altcha_public_key; - } - /** - * Loads the altcha libraries. - * - * @returns {Promise|Boolean} promise if libs are loading else false if the altcha key is empty. - */ - loadLibs() { - if (this._publicKey) { - this._altchaReady = loadBundle(`web.altcha_libs`); - return this._altchaReady.then(() => - Boolean(document.querySelector(".o_altcha_widget")) - ); - } - return false; - } -} diff --git a/website_altcha/static/src/generic.esm.js b/website_altcha/static/src/generic.esm.js new file mode 100644 index 0000000000..c1130b6845 --- /dev/null +++ b/website_altcha/static/src/generic.esm.js @@ -0,0 +1,9 @@ +import {AltchaLegacyClassFunctionality} from "@website_altcha/altcha.esm"; +import publicWidget from "@web/legacy/js/public/public_widget"; + +publicWidget.registry.GenericAltcha = publicWidget.Widget.extend({ + ...AltchaLegacyClassFunctionality, + selector: "form.website_altcha_form, form.js_website_submit_form", + altcha_prepend_to: + "*:has( > input[type='submit']), *:has( > button[type='submit'])", +}); diff --git a/website_altcha/static/src/signup.esm.js b/website_altcha/static/src/signup.esm.js new file mode 100644 index 0000000000..49954f71b2 --- /dev/null +++ b/website_altcha/static/src/signup.esm.js @@ -0,0 +1,12 @@ +import {AltchaLegacyClassFunctionality} from "./altcha.esm"; +import publicWidget from "@web/legacy/js/public/public_widget"; + +publicWidget.registry.SignupAltcha = publicWidget.Widget.extend({ + ...AltchaLegacyClassFunctionality, + selector: ".oe_signup_form", +}); + +publicWidget.registry.ResetPasswordAltcha = publicWidget.Widget.extend({ + ...AltchaLegacyClassFunctionality, + selector: ".oe_reset_password_form", +}); diff --git a/website_altcha/static/src/signup/signup.esm.js b/website_altcha/static/src/signup/signup.esm.js deleted file mode 100644 index fb5de1032d..0000000000 --- a/website_altcha/static/src/signup/signup.esm.js +++ /dev/null @@ -1,73 +0,0 @@ -import "@website/snippets/s_website_form/000"; -import {Altcha} from "@website_altcha/altcha/altcha.esm"; -import publicWidget from "@web/legacy/js/public/public_widget"; -import {renderToString} from "@web/core/utils/render"; - -export const AltchaFunctionality = { - init() { - this._super(...arguments); - this._altcha = new Altcha(); - }, - - async willStart() { - this._altcha.loadLibs(); - return this._super(...arguments); - }, - start: function () { - if (this._altcha._publicKey && !this.$el.find(".o_altcha_widget").length) { - this.$el - .find("div.oe_login_buttons") - .prepend(renderToString("website_altcha.AltchaWidget", {})); - } - return this._super(...arguments); - }, -}; - -publicWidget.registry.SignupAltcha = publicWidget.Widget.extend({ - ...AltchaFunctionality, - selector: ".oe_signup_form", -}); - -publicWidget.registry.ResetPasswordAltcha = publicWidget.Widget.extend({ - ...AltchaFunctionality, - selector: ".oe_reset_password_form", -}); - -publicWidget.registry.s_website_form.include({ - init() { - this._super(...arguments); - this._altcha = new Altcha(); - }, - - async willStart() { - this._altcha.loadLibs(); - return this._super(...arguments); - }, - - /** - * @override - */ - start: function () { - const res = this._super(...arguments); - if (this.$target[0].classList.contains("s_website_form_no_recaptcha")) { - return res; - } - if (this._altcha._publicKey) { - this.$el - .find(".s_website_form_submit") - .before(renderToString("website_altcha.AltchaWidget", {})); - } - return res; - }, -}); -publicWidget.registry.EditModeWebsiteForm.include({ - start: function () { - const res = this._super(...arguments); - if (this.editableMode) { - // We should delete the altcha widget in edit mode - this.$el.find(".o_altcha_widget").remove(); - this.$el.find(".o_altcha_widget_container").remove(); - } - return res; - }, -}); diff --git a/website_altcha/static/src/website_form.esm.js b/website_altcha/static/src/website_form.esm.js new file mode 100644 index 0000000000..f97201195d --- /dev/null +++ b/website_altcha/static/src/website_form.esm.js @@ -0,0 +1,36 @@ +import "@website/snippets/s_website_form/000"; +import {AltchaLegacyClassFunctionality} from "./altcha.esm"; +import publicWidget from "@web/legacy/js/public/public_widget"; +import {renderToString} from "@web/core/utils/render"; + +publicWidget.registry.s_website_form.include({ + ...AltchaLegacyClassFunctionality, +}); +publicWidget.registry.s_website_form.include({ + altcha_insert_widget() { + // Intentional no-op + }, + start: function () { + const res = this._super(...arguments); + if (this.$target[0].classList.contains("s_website_form_no_recaptcha")) { + return res; + } + if (this.altcha_enabled) { + this.$el + .find(".s_website_form_submit") + .before(renderToString("website_altcha.AltchaWidget", {})); + } + return res; + }, +}); +publicWidget.registry.EditModeWebsiteForm.include({ + start: function () { + const res = this._super(...arguments); + if (this.editableMode) { + // We should delete the altcha widget in edit mode + this.$el.find(".o_altcha_widget").remove(); + this.$el.find(".o_altcha_widget_container").remove(); + } + return res; + }, +}); diff --git a/website_altcha/static/tests/tours/website_altcha_contact.esm.js b/website_altcha/static/tests/tours/website_altcha_contact.esm.js new file mode 100644 index 0000000000..68321c3d08 --- /dev/null +++ b/website_altcha/static/tests/tours/website_altcha_contact.esm.js @@ -0,0 +1,41 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import {registry} from "@web/core/registry"; + +registry.category("web_tour.tours").add("website_altcha_contact", { + steps: () => [ + { + content: "Fill in a name", + trigger: "input[name='name']", + run: "fill Testname", + }, + { + content: "Fill in an email", + trigger: "input[name='email_from']", + run: "fill test_website_altcha@test.com", + }, + { + content: "Fill in a subject", + trigger: "input[name='subject']", + run: "fill the subject", + }, + { + content: "Fill in a question", + trigger: "textarea[name='description']", + run: "fill the question", + }, + { + content: "Verify altcha", + trigger: "altcha-widget input[type='checkbox']", + run: "click", + }, + { + content: "Verify altcha value exists", + trigger: "input[name='altcha']:not(:visible):not(:empty)", + }, + { + content: "Click Submit", + trigger: "div.s_website_form_submit a", + run: "click", + }, + ], +}); diff --git a/website_altcha/static/tests/tours/website_altcha_generic.esm.js b/website_altcha/static/tests/tours/website_altcha_generic.esm.js new file mode 100644 index 0000000000..3d4ad89cf0 --- /dev/null +++ b/website_altcha/static/tests/tours/website_altcha_generic.esm.js @@ -0,0 +1,21 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import {registry} from "@web/core/registry"; + +registry.category("web_tour.tours").add("website_altcha_generic", { + steps: () => [ + { + content: "Verify altcha", + trigger: "altcha-widget input[type='checkbox']", + run: "click", + }, + { + content: "Verify altcha value exists", + trigger: "input[name='altcha']:not(:visible):not(:empty)", + }, + { + content: "Click Submit", + trigger: "button[type='submit']", + run: "click", + }, + ], +}); diff --git a/website_altcha/static/tests/tours/website_altcha_signup.esm.js b/website_altcha/static/tests/tours/website_altcha_signup.esm.js new file mode 100644 index 0000000000..824087f07d --- /dev/null +++ b/website_altcha/static/tests/tours/website_altcha_signup.esm.js @@ -0,0 +1,26 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import {registry} from "@web/core/registry"; + +registry.category("web_tour.tours").add("website_altcha_signup", { + steps: () => [ + { + content: "Fill in a login", + trigger: "input[name='login']", + run: "fill admin", + }, + { + content: "Verify altcha", + trigger: "altcha-widget input[type='checkbox']", + run: "click", + }, + { + content: "Verify altcha value exists", + trigger: "input[name='altcha']:not(:visible):not(:empty)", + }, + { + content: "Click Submit", + trigger: "button[type='submit']", + run: "click", + }, + ], +}); diff --git a/website_altcha/tests/__init__.py b/website_altcha/tests/__init__.py index 65d22d8cf1..2fb64ca391 100644 --- a/website_altcha/tests/__init__.py +++ b/website_altcha/tests/__init__.py @@ -1 +1,2 @@ from . import test_altcha +from . import test_altcha_tours diff --git a/website_altcha/tests/common.py b/website_altcha/tests/common.py new file mode 100644 index 0000000000..dadd8112b2 --- /dev/null +++ b/website_altcha/tests/common.py @@ -0,0 +1,18 @@ +# Copyright 2026 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import HttpCase, tagged + + +@tagged("-at_install", "post_install") +class Common(HttpCase): + @classmethod + def setUpClass(cls): + result = super().setUpClass() + cls.website = cls.env["website"].get_current_website() + cls.website.altcha_key = "test_key" + cls.website.altcha_private_key = "test_secret_key" + cls.website.altcha_cost = 2 + # Forcing a small power of 2 cost for allowing scrypt to work + # and make everything faster + return result diff --git a/website_altcha/tests/test_altcha.py b/website_altcha/tests/test_altcha.py index 462f16638b..0fea6cb962 100644 --- a/website_altcha/tests/test_altcha.py +++ b/website_altcha/tests/test_altcha.py @@ -8,13 +8,15 @@ from requests.exceptions import HTTPError from odoo.exceptions import UserError -from odoo.tests import Form, HttpCase +from odoo.tests import Form from odoo.tools import mute_logger from odoo.addons.website.tools import MockRequest +from .common import Common -class TestAltchaCreation(HttpCase): + +class TestAltchaCreation(Common): @classmethod def setUpClass(cls): result = super().setUpClass() diff --git a/website_altcha/tests/test_altcha_tours.py b/website_altcha/tests/test_altcha_tours.py new file mode 100644 index 0000000000..4cfeef523e --- /dev/null +++ b/website_altcha/tests/test_altcha_tours.py @@ -0,0 +1,24 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.website_altcha.tests.common import Common + + +class TestWebsiteAltchaTours(Common): + def test_contact_tour(self): + """ + Run the contact test tour + """ + self.start_tour("/contactus", "website_altcha_contact") + + def test_signup_tour(self): + """ + Run the signup test tour + """ + self.start_tour("/web/reset_password", "website_altcha_signup") + + def test_generic_tour(self): + """ + Run the generic test tour + """ + self.start_tour("/website_altcha_demo", "website_altcha_generic") diff --git a/website_event_altcha/README.rst b/website_event_altcha/README.rst new file mode 100644 index 0000000000..fc6fce375b --- /dev/null +++ b/website_event_altcha/README.rst @@ -0,0 +1,85 @@ +============================================= +Privacy Friendly Captcha - Event Registration +============================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:6ed4773e07efe30601ad749fad1acac11076acd30f5741f042de4108ef01205a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_event_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_event_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 event registration forms + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Hunki Enterprises BV + +Contributors +------------ + +- Holger Brunn + (https://hunki-enterprises.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. + +.. |maintainer-hbrunn| image:: https://github.com/hbrunn.png?size=40px + :target: https://github.com/hbrunn + :alt: hbrunn + +Current `maintainer `__: + +|maintainer-hbrunn| + +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_event_altcha/__init__.py b/website_event_altcha/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website_event_altcha/__manifest__.py b/website_event_altcha/__manifest__.py new file mode 100644 index 0000000000..a4d93c073e --- /dev/null +++ b/website_event_altcha/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Privacy Friendly Captcha - Event Registration", + "summary": "Support event registration with ALTCHA", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Hunki Enterprises BV,Odoo Community Association (OCA)", + "maintainers": ["hbrunn"], + "website": "https://github.com/OCA/website", + "depends": [ + "website_altcha", + "website_event", + ], + "assets": { + "web.assets_frontend": [ + "website_event_altcha/static/src/*.esm.js", + ], + "web.assets_tests": [ + "website_event_altcha/static/tests/tours/website_event_altcha.esm.js", + ], + }, + "auto_install": True, +} diff --git a/website_event_altcha/pyproject.toml b/website_event_altcha/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/website_event_altcha/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/website_event_altcha/readme/CONTRIBUTORS.md b/website_event_altcha/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..cf8bf7205a --- /dev/null +++ b/website_event_altcha/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Holger Brunn \ (https://hunki-enterprises.com) diff --git a/website_event_altcha/readme/DESCRIPTION.md b/website_event_altcha/readme/DESCRIPTION.md new file mode 100644 index 0000000000..33a3690849 --- /dev/null +++ b/website_event_altcha/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module adds ALTCHA support to event registration forms diff --git a/website_event_altcha/static/description/icon.png b/website_event_altcha/static/description/icon.png new file mode 100644 index 0000000000..d8e30bafa5 Binary files /dev/null and b/website_event_altcha/static/description/icon.png differ diff --git a/website_event_altcha/static/description/index.html b/website_event_altcha/static/description/index.html new file mode 100644 index 0000000000..f9f8a7dd17 --- /dev/null +++ b/website_event_altcha/static/description/index.html @@ -0,0 +1,426 @@ + + + + + +Privacy Friendly Captcha - Event Registration + + + +
+

Privacy Friendly Captcha - Event Registration

+ + +

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

+

This module adds ALTCHA support to event registration forms

+

Table of contents

+ +
+

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

+
    +
  • Hunki Enterprises BV
  • +
+
+ +
+

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.

+

Current maintainer:

+

hbrunn

+

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_event_altcha/static/src/EventRegistrationForm.esm.js b/website_event_altcha/static/src/EventRegistrationForm.esm.js new file mode 100644 index 0000000000..f375ef6324 --- /dev/null +++ b/website_event_altcha/static/src/EventRegistrationForm.esm.js @@ -0,0 +1,32 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import {AltchaLegacyClassFunctionality} from "@website_altcha/altcha.esm"; +import EventRegistrationForm from "@website_event/js/website_event"; + +EventRegistrationForm.include({ + ...AltchaLegacyClassFunctionality, +}); + +EventRegistrationForm.include({ + altcha_prepend_to: "div.modal-footer", + start() { + const result = this._super(...arguments); + const altcha_widget = this.el.querySelector("altcha-widget"); + if (altcha_widget) { + const button = this.el.querySelector("button.btn-primary"); + altcha_widget.addEventListener("statechange", (ev) => { + button.disabled = ev.detail.state !== "verified"; + }); + altcha_widget.addEventListener("load", () => { + button.disabled = altcha_widget.getState() !== "verified"; + }); + } + return result; + }, + _addTurnstile(form) { + const altcha_widget = this.el.querySelector("altcha-widget"); + if (altcha_widget) { + form.querySelector("div.modal-footer").prepend(altcha_widget); + } + this._super(...arguments); + }, +}); diff --git a/website_event_altcha/static/src/ticketDetailsWidget.esm.js b/website_event_altcha/static/src/ticketDetailsWidget.esm.js new file mode 100644 index 0000000000..0a3c3548e0 --- /dev/null +++ b/website_event_altcha/static/src/ticketDetailsWidget.esm.js @@ -0,0 +1,20 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import ticketDetailsWidget from "@website_event/js/website_event_ticket_details"; + +ticketDetailsWidget.include({ + start() { + this.disableSubmitIfUnverified(); + return this._super(...arguments); + }, + _onTicketQuantityChange() { + this._super(...arguments); + this.disableSubmitIfUnverified(); + }, + disableSubmitIfUnverified() { + const altcha_widget = this.el.querySelector("altcha-widget"); + if (altcha_widget) { + const button = this.el.querySelector("button.btn-primary"); + button.disabled = altcha_widget.getState() !== "verified"; + } + }, +}); diff --git a/website_event_altcha/static/tests/tours/website_event_altcha.esm.js b/website_event_altcha/static/tests/tours/website_event_altcha.esm.js new file mode 100644 index 0000000000..74983b2a3f --- /dev/null +++ b/website_event_altcha/static/tests/tours/website_event_altcha.esm.js @@ -0,0 +1,51 @@ +/* Copyright 2026 Hunki Enterprises BV */ +import {registry} from "@web/core/registry"; + +registry.category("web_tour.tours").add("website_event_altcha", { + steps: () => [ + { + content: "Click register button", + trigger: "button[data-bs-target='#modal_ticket_registration']", + run: "click", + }, + { + content: "Select a ticket", + trigger: "#o_wevent_tickets_collapse select", + run: "selectByIndex 1", + }, + { + content: "Verify altcha", + trigger: "altcha-widget input[type='checkbox']", + run: "click", + }, + { + content: "Click Submit", + trigger: "#registration_form button[type='submit']:enabled", + run: "click", + }, + { + content: "Fill in name", + trigger: "#attendee_registration input[name*='-name-']:enabled", + async run() { + this.anchor.value = "Testname"; + }, + }, + { + content: "Fill in email", + trigger: "#attendee_registration input[name*='-email-']:enabled", + async run() { + this.anchor.value = "test_website_altcha@test.com"; + }, + }, + { + content: "Verify altcha value exists", + trigger: + "#attendee_registration input[name='altcha']:not(:visible):not(:empty)", + }, + { + content: "Click submit", + trigger: "#attendee_registration button[type='submit']", + run: "click", + }, + ], +}); diff --git a/website_event_altcha/tests/__init__.py b/website_event_altcha/tests/__init__.py new file mode 100644 index 0000000000..75a0c88ebe --- /dev/null +++ b/website_event_altcha/tests/__init__.py @@ -0,0 +1 @@ +from . import test_website_event_altcha diff --git a/website_event_altcha/tests/test_website_event_altcha.py b/website_event_altcha/tests/test_website_event_altcha.py new file mode 100644 index 0000000000..f23594165b --- /dev/null +++ b/website_event_altcha/tests/test_website_event_altcha.py @@ -0,0 +1,13 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.website_altcha.tests.common import Common + + +class TestWebsiteEventAltcha(Common): + def test_event_registration_with_questions(self): + """ + Test an event registration that contains questions + """ + event = self.env.ref("event.event_7") + self.start_tour(f"/event/{event.id}", "website_event_altcha")