From fcc83968b6b1d3996b4e3b5e5d355ffc0aac32ec Mon Sep 17 00:00:00 2001 From: Riccardo Fiore Date: Thu, 25 Jun 2026 23:04:51 +0200 Subject: [PATCH 1/2] [ADD] website_event_custom_registration Add a per-event "Registration Mode" (native, free or external) on website events. The registration block, the navbar call to action and the backend event form adapt to the selected mode: a free event shows a custom message, an external event links out to a foreign ticket portal, and native events keep Odoo's standard ticketing. In non-native modes the native seat limit is read-only and the registration smart buttons are hidden. --- website_event_custom_registration/README.rst | 145 ++++++ website_event_custom_registration/__init__.py | 1 + .../__manifest__.py | 20 + .../website_event_custom_registration.pot | 109 ++++ .../models/__init__.py | 1 + .../models/event_event.py | 41 ++ .../pyproject.toml | 3 + .../readme/CONFIGURE.md | 11 + .../readme/CONTRIBUTORS.md | 1 + .../readme/DESCRIPTION.md | 20 + .../readme/USAGE.md | 12 + .../static/description/index.html | 487 ++++++++++++++++++ .../tests/__init__.py | 1 + .../tests/test_event_custom_registration.py | 66 +++ .../views/event_event_views.xml | 68 +++ .../views/website_event_templates.xml | 94 ++++ 16 files changed, 1080 insertions(+) create mode 100644 website_event_custom_registration/README.rst create mode 100644 website_event_custom_registration/__init__.py create mode 100644 website_event_custom_registration/__manifest__.py create mode 100644 website_event_custom_registration/i18n/website_event_custom_registration.pot create mode 100644 website_event_custom_registration/models/__init__.py create mode 100644 website_event_custom_registration/models/event_event.py create mode 100644 website_event_custom_registration/pyproject.toml create mode 100644 website_event_custom_registration/readme/CONFIGURE.md create mode 100644 website_event_custom_registration/readme/CONTRIBUTORS.md create mode 100644 website_event_custom_registration/readme/DESCRIPTION.md create mode 100644 website_event_custom_registration/readme/USAGE.md create mode 100644 website_event_custom_registration/static/description/index.html create mode 100644 website_event_custom_registration/tests/__init__.py create mode 100644 website_event_custom_registration/tests/test_event_custom_registration.py create mode 100644 website_event_custom_registration/views/event_event_views.xml create mode 100644 website_event_custom_registration/views/website_event_templates.xml diff --git a/website_event_custom_registration/README.rst b/website_event_custom_registration/README.rst new file mode 100644 index 000000000..062328aec --- /dev/null +++ b/website_event_custom_registration/README.rst @@ -0,0 +1,145 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Website Event Custom Registration +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:606fc643f798562a7407fd35e50661c5d3714df63c648b1850bf06c94369aff8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fevent-lightgray.png?logo=github + :target: https://github.com/OCA/event/tree/19.0/website_event_custom_registration + :alt: OCA/event +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/event-19-0/event-19-0-website_event_custom_registration + :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/event&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module lets each website event choose how attendees register, +beyond Odoo's native ticketing, through a single ``Registration Mode`` +selection: + +- **Native ticketing**: Odoo's standard tickets and registration flow + (default, unchanged behaviour). +- **Free / No registration**: no tickets; a custom rich-text message is + shown in the registration block instead. +- **External registration**: a button redirects attendees to a foreign + ticket portal in a new tab. + +The three behaviours are combined into one Selection field to avoid the +UI state clashes and XPath conflicts that separate modules would create. + +When an event is *free* or *external*, the native "Sold Out" / +"Registrations Closed" labels and the navbar register link are +suppressed, so the page never looks broken just because no native ticket +is configured. + +When a non-native mode is selected, the backend event form also locks +the native seat limit read-only and hides the registration statistics +and attendees smart buttons, since no native registrations are +collected. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure an event: + +1. Go to *Events* and open (or create) an event. +2. In the **Registration** group, choose a **Registration Mode**: + + - **Native ticketing**: configure tickets as usual in the *Tickets* + tab. + - **Free / No registration**: fill in the **Free Event Message** + shown to visitors. + - **External registration**: fill in the **External Ticket URL** and, + optionally, the **External Button Text** (defaults to + ``Get Tickets``). + +3. Save and publish the event. + +Usage +===== + +On the public event page, the registration block adapts to the selected +mode: + +- **Native ticketing**: the standard *Register* button and ticket modal. +- **Free / No registration**: the configured message, with no ticket + controls and no "Sold Out" / "Closed" labels. +- **External registration**: a *Get Tickets* button linking to the + external portal, opening in a new tab. + +The routing applies everywhere the core registration block is rendered: +the event page's main call to action, the (desktop and mobile) sidebar, +and the event sub-menu, so the experience stays consistent across the +site. + +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 +------- + +* Riccardo Fiore + +Contributors +------------ + +- Riccardo Fiore (Odrakir) + +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-odrakirmusic| image:: https://github.com/odrakirmusic.png?size=40px + :target: https://github.com/odrakirmusic + :alt: odrakirmusic + +Current `maintainer `__: + +|maintainer-odrakirmusic| + +This module is part of the `OCA/event `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_event_custom_registration/__init__.py b/website_event_custom_registration/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/website_event_custom_registration/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/website_event_custom_registration/__manifest__.py b/website_event_custom_registration/__manifest__.py new file mode 100644 index 000000000..4ffb0235d --- /dev/null +++ b/website_event_custom_registration/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2026 Riccardo Fiore +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Website Event Custom Registration", + "summary": "Choose free, native or external ticketing per website event", + "version": "19.0.1.0.0", + "category": "Marketing/Events", + "author": "Riccardo Fiore, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/event", + "license": "AGPL-3", + "development_status": "Beta", + "maintainers": ["odrakirmusic"], + "depends": ["website_event"], + "data": [ + "views/event_event_views.xml", + "views/website_event_templates.xml", + ], + "installable": True, + "application": False, +} diff --git a/website_event_custom_registration/i18n/website_event_custom_registration.pot b/website_event_custom_registration/i18n/website_event_custom_registration.pot new file mode 100644 index 000000000..c8b693892 --- /dev/null +++ b/website_event_custom_registration/i18n/website_event_custom_registration.pot @@ -0,0 +1,109 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_event_custom_registration +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0-20260609\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-06-21 15:14+0000\n" +"PO-Revision-Date: 2026-06-21 15:14+0000\n" +"Last-Translator: \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_event_custom_registration +#: model:ir.model.fields,help:website_event_custom_registration.field_event_event__external_ticket_url +msgid "" +"Address of the external ticketing portal attendees are sent to when the " +"registration mode is 'External registration'." +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__display_name +msgid "Display Name" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model,name:website_event_custom_registration.model_event_event +msgid "Event" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__external_button_text +msgid "External Button Text" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__external_ticket_url +msgid "External Ticket URL" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields.selection,name:website_event_custom_registration.selection__event_event__registration_mode__external +msgid "External registration" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields.selection,name:website_event_custom_registration.selection__event_event__registration_mode__free +msgid "Free / No registration" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__free_event_text +msgid "Free Event Message" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,help:website_event_custom_registration.field_event_event__registration_mode +msgid "" +"How attendees register for this event on the website:\n" +"- Native ticketing: use Odoo's built-in tickets and registration.\n" +"- Free / No registration: show a custom message instead of tickets.\n" +"- External registration: redirect attendees to a foreign ticket portal." +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__id +msgid "ID" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,help:website_event_custom_registration.field_event_event__external_button_text +msgid "Label of the button that links to the external ticketing portal." +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,help:website_event_custom_registration.field_event_event__free_event_text +msgid "" +"Message displayed on the event page when the registration mode is 'Free / No" +" registration'." +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields.selection,name:website_event_custom_registration.selection__event_event__registration_mode__native +msgid "Native ticketing" +msgstr "" + +#. module: website_event_custom_registration +#: model_terms:ir.ui.view,arch_db:website_event_custom_registration.view_event_form +msgid "Registration" +msgstr "" + +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__registration_mode +msgid "Registration Mode" +msgstr "" + +#. module: website_event_custom_registration +#: model_terms:ir.ui.view,arch_db:website_event_custom_registration.view_event_form +msgid "e.g. Free entry — just show up on the night." +msgstr "" + +#. module: website_event_custom_registration +#: model_terms:ir.ui.view,arch_db:website_event_custom_registration.view_event_form +msgid "https://tickets.example.com/my-event" +msgstr "" diff --git a/website_event_custom_registration/models/__init__.py b/website_event_custom_registration/models/__init__.py new file mode 100644 index 000000000..b6bc38997 --- /dev/null +++ b/website_event_custom_registration/models/__init__.py @@ -0,0 +1 @@ +from . import event_event diff --git a/website_event_custom_registration/models/event_event.py b/website_event_custom_registration/models/event_event.py new file mode 100644 index 000000000..278770e5f --- /dev/null +++ b/website_event_custom_registration/models/event_event.py @@ -0,0 +1,41 @@ +# Copyright 2026 Riccardo Fiore +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class EventEvent(models.Model): + _inherit = "event.event" + + registration_mode = fields.Selection( + selection=[ + ("native", "Native ticketing"), + ("free", "Free / No registration"), + ("external", "External registration"), + ], + default="native", + required=True, + help=( + "How attendees register for this event on the website:\n" + "- Native ticketing: use Odoo's built-in tickets and registration.\n" + "- Free / No registration: show a custom message instead of tickets.\n" + "- External registration: redirect attendees to a foreign ticket " + "portal." + ), + ) + free_event_text = fields.Html( + string="Free Event Message", + translate=True, + sanitize_attributes=True, + help="Message displayed on the event page when the registration mode " + "is 'Free / No registration'.", + ) + external_ticket_url = fields.Char( + string="External Ticket URL", + help="Address of the external ticketing portal attendees are sent to " + "when the registration mode is 'External registration'.", + ) + external_button_text = fields.Char( + translate=True, + default="Get Tickets", + help="Label of the button that links to the external ticketing portal.", + ) diff --git a/website_event_custom_registration/pyproject.toml b/website_event_custom_registration/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/website_event_custom_registration/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/website_event_custom_registration/readme/CONFIGURE.md b/website_event_custom_registration/readme/CONFIGURE.md new file mode 100644 index 000000000..ae10a9295 --- /dev/null +++ b/website_event_custom_registration/readme/CONFIGURE.md @@ -0,0 +1,11 @@ +To configure an event: + +1. Go to *Events* and open (or create) an event. +2. In the **Registration** group, choose a **Registration Mode**: + - **Native ticketing**: configure tickets as usual in the *Tickets* + tab. + - **Free / No registration**: fill in the **Free Event Message** shown + to visitors. + - **External registration**: fill in the **External Ticket URL** and, + optionally, the **External Button Text** (defaults to `Get Tickets`). +3. Save and publish the event. diff --git a/website_event_custom_registration/readme/CONTRIBUTORS.md b/website_event_custom_registration/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..14d07c072 --- /dev/null +++ b/website_event_custom_registration/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Riccardo Fiore (Odrakir) \ diff --git a/website_event_custom_registration/readme/DESCRIPTION.md b/website_event_custom_registration/readme/DESCRIPTION.md new file mode 100644 index 000000000..a4012ec1a --- /dev/null +++ b/website_event_custom_registration/readme/DESCRIPTION.md @@ -0,0 +1,20 @@ +This module lets each website event choose how attendees register, beyond +Odoo's native ticketing, through a single `Registration Mode` selection: + +- **Native ticketing**: Odoo's standard tickets and registration flow + (default, unchanged behaviour). +- **Free / No registration**: no tickets; a custom rich-text message is + shown in the registration block instead. +- **External registration**: a button redirects attendees to a foreign + ticket portal in a new tab. + +The three behaviours are combined into one Selection field to avoid the UI +state clashes and XPath conflicts that separate modules would create. + +When an event is *free* or *external*, the native "Sold Out" / +"Registrations Closed" labels and the navbar register link are suppressed, +so the page never looks broken just because no native ticket is configured. + +When a non-native mode is selected, the backend event form also locks the +native seat limit read-only and hides the registration statistics and +attendees smart buttons, since no native registrations are collected. diff --git a/website_event_custom_registration/readme/USAGE.md b/website_event_custom_registration/readme/USAGE.md new file mode 100644 index 000000000..3c28987e0 --- /dev/null +++ b/website_event_custom_registration/readme/USAGE.md @@ -0,0 +1,12 @@ +On the public event page, the registration block adapts to the selected +mode: + +- **Native ticketing**: the standard *Register* button and ticket modal. +- **Free / No registration**: the configured message, with no ticket + controls and no "Sold Out" / "Closed" labels. +- **External registration**: a *Get Tickets* button linking to the external + portal, opening in a new tab. + +The routing applies everywhere the core registration block is rendered: the +event page's main call to action, the (desktop and mobile) sidebar, and the +event sub-menu, so the experience stays consistent across the site. diff --git a/website_event_custom_registration/static/description/index.html b/website_event_custom_registration/static/description/index.html new file mode 100644 index 000000000..a9dbd1359 --- /dev/null +++ b/website_event_custom_registration/static/description/index.html @@ -0,0 +1,487 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Website Event Custom Registration

+ +

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

+

This module lets each website event choose how attendees register, +beyond Odoo’s native ticketing, through a single Registration Mode +selection:

+
    +
  • Native ticketing: Odoo’s standard tickets and registration flow +(default, unchanged behaviour).
  • +
  • Free / No registration: no tickets; a custom rich-text message is +shown in the registration block instead.
  • +
  • External registration: a button redirects attendees to a foreign +ticket portal in a new tab.
  • +
+

The three behaviours are combined into one Selection field to avoid the +UI state clashes and XPath conflicts that separate modules would create.

+

When an event is free or external, the native “Sold Out” / +“Registrations Closed” labels and the navbar register link are +suppressed, so the page never looks broken just because no native ticket +is configured.

+

When a non-native mode is selected, the backend event form also locks +the native seat limit read-only and hides the registration statistics +and attendees smart buttons, since no native registrations are +collected.

+

Table of contents

+ +
+

Configuration

+

To configure an event:

+
    +
  1. Go to Events and open (or create) an event.
  2. +
  3. In the Registration group, choose a Registration Mode:
      +
    • Native ticketing: configure tickets as usual in the Tickets +tab.
    • +
    • Free / No registration: fill in the Free Event Message +shown to visitors.
    • +
    • External registration: fill in the External Ticket URL and, +optionally, the External Button Text (defaults to +Get Tickets).
    • +
    +
  4. +
  5. Save and publish the event.
  6. +
+
+
+

Usage

+

On the public event page, the registration block adapts to the selected +mode:

+
    +
  • Native ticketing: the standard Register button and ticket modal.
  • +
  • Free / No registration: the configured message, with no ticket +controls and no “Sold Out” / “Closed” labels.
  • +
  • External registration: a Get Tickets button linking to the +external portal, opening in a new tab.
  • +
+

The routing applies everywhere the core registration block is rendered: +the event page’s main call to action, the (desktop and mobile) sidebar, +and the event sub-menu, so the experience stays consistent across the +site.

+
+
+

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

+
    +
  • Riccardo Fiore
  • +
+
+
+

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.

+

Current maintainer:

+

odrakirmusic

+

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

+

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

+
+
+
+
+ + diff --git a/website_event_custom_registration/tests/__init__.py b/website_event_custom_registration/tests/__init__.py new file mode 100644 index 000000000..e35e20026 --- /dev/null +++ b/website_event_custom_registration/tests/__init__.py @@ -0,0 +1 @@ +from . import test_event_custom_registration diff --git a/website_event_custom_registration/tests/test_event_custom_registration.py b/website_event_custom_registration/tests/test_event_custom_registration.py new file mode 100644 index 000000000..1a4f07396 --- /dev/null +++ b/website_event_custom_registration/tests/test_event_custom_registration.py @@ -0,0 +1,66 @@ +# Copyright 2026 Riccardo Fiore +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests.common import TransactionCase + + +class TestEventCustomRegistration(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.event = cls.env["event.event"].create( + { + "name": "Test DJ Night", + "date_begin": "2099-01-01 20:00:00", + "date_end": "2099-01-02 02:00:00", + } + ) + + def _render_registration(self): + return str( + self.env["ir.qweb"]._render( + "website_event.registration_template", + { + "event": self.event, + "event_page": True, + "cta_additional_classes": "", + "registration_error_code": False, + }, + ) + ) + + def test_defaults(self): + self.assertEqual(self.event.registration_mode, "native") + self.assertEqual(self.event.external_button_text, "Get Tickets") + + def test_free_mode_renders_message_and_hides_native_labels(self): + self.event.write( + { + "registration_mode": "free", + "free_event_text": "

Free entry, just turn up

", + } + ) + html = self._render_registration() + self.assertIn("Free entry, just turn up", html) + self.assertIn("o_wevent_free_registration", html) + # No native ticketing chrome leaks through. + self.assertNotIn("Sold Out", html) + self.assertNotIn("Registrations Closed", html) + + def test_external_mode_renders_outbound_button(self): + self.event.write( + { + "registration_mode": "external", + "external_ticket_url": "https://tickets.example.com/dj-night", + "external_button_text": "Buy on Resident Advisor", + } + ) + html = self._render_registration() + self.assertIn("https://tickets.example.com/dj-night", html) + self.assertIn("Buy on Resident Advisor", html) + self.assertIn('target="_blank"', html) + self.assertNotIn("Sold Out", html) + + def test_native_mode_does_not_leak_custom_markup(self): + self.event.registration_mode = "native" + html = self._render_registration() + self.assertNotIn("o_wevent_free_registration", html) diff --git a/website_event_custom_registration/views/event_event_views.xml b/website_event_custom_registration/views/event_event_views.xml new file mode 100644 index 000000000..4b55b9be4 --- /dev/null +++ b/website_event_custom_registration/views/event_event_views.xml @@ -0,0 +1,68 @@ + + + + + event.event.form.custom.registration + event.event + + + + + + + + + + + + + + + registration_mode != 'native' + + + registration_mode != 'native' + + + + + registration_mode != 'native' + + + registration_mode != 'native' + + + + diff --git a/website_event_custom_registration/views/website_event_templates.xml b/website_event_custom_registration/views/website_event_templates.xml new file mode 100644 index 000000000..8463ca76e --- /dev/null +++ b/website_event_custom_registration/views/website_event_templates.xml @@ -0,0 +1,94 @@ + + + + + + + + + From 1b1889dc0c09a3706841e8f5a5a24d285cc5bdf8 Mon Sep 17 00:00:00 2001 From: Riccardo Fiore Date: Wed, 1 Jul 2026 22:56:12 +0200 Subject: [PATCH 2/2] [IMP] website_event_custom_registration: option to keep custom text when closed Once registrations close (past, sold-out or cancelled event), free and external events now show Odoo's standard 'Registrations Closed' notice like native ones, instead of keeping their custom content forever. A new per-event boolean keep_custom_text_when_closed (backend toggle, visible for the free/external modes only) restores the previous behaviour: the free event message or the external ticket button stays visible after registrations close. --- website_event_custom_registration/README.rst | 20 ++++++-- .../website_event_custom_registration.pot | 14 ++++++ .../models/event_event.py | 8 ++++ .../readme/CONFIGURE.md | 6 ++- .../readme/DESCRIPTION.md | 8 +++- .../readme/USAGE.md | 5 ++ .../static/description/index.html | 17 ++++++- .../tests/test_event_custom_registration.py | 47 +++++++++++++++++++ .../views/event_event_views.xml | 7 +++ .../views/website_event_templates.xml | 23 ++++++--- 10 files changed, 140 insertions(+), 15 deletions(-) diff --git a/website_event_custom_registration/README.rst b/website_event_custom_registration/README.rst index 062328aec..64ced09d8 100644 --- a/website_event_custom_registration/README.rst +++ b/website_event_custom_registration/README.rst @@ -48,8 +48,12 @@ UI state clashes and XPath conflicts that separate modules would create. When an event is *free* or *external*, the native "Sold Out" / "Registrations Closed" labels and the navbar register link are -suppressed, so the page never looks broken just because no native ticket -is configured. +suppressed while registrations are open, so the page never looks broken +just because no native ticket is configured. Once registrations close +(the event has ended, is sold out or was cancelled), Odoo's standard +"Registrations Closed" notice takes over by default; a per-event **Keep +Custom Text When Closed** toggle keeps the custom message or button +visible instead. When a non-native mode is selected, the backend event form also locks the native seat limit read-only and hides the registration statistics @@ -77,7 +81,11 @@ To configure an event: optionally, the **External Button Text** (defaults to ``Get Tickets``). -3. Save and publish the event. +3. For *free* and *external* events, optionally enable **Keep Custom + Text When Closed** to keep the custom message or button visible after + registrations close (past, sold-out or cancelled event), instead of + Odoo's standard "Registrations Closed" notice. +4. Save and publish the event. Usage ===== @@ -91,6 +99,12 @@ mode: - **External registration**: a *Get Tickets* button linking to the external portal, opening in a new tab. +Once registrations close (the event has ended, is sold out or was +cancelled), free and external events show Odoo's standard "Registrations +Closed" notice like native ones, unless **Keep Custom Text When Closed** +is enabled on the event — then the custom message or button stays +visible. + The routing applies everywhere the core registration block is rendered: the event page's main call to action, the (desktop and mobile) sidebar, and the event sub-menu, so the experience stays consistent across the diff --git a/website_event_custom_registration/i18n/website_event_custom_registration.pot b/website_event_custom_registration/i18n/website_event_custom_registration.pot index c8b693892..9105b9a66 100644 --- a/website_event_custom_registration/i18n/website_event_custom_registration.pot +++ b/website_event_custom_registration/i18n/website_event_custom_registration.pot @@ -71,6 +71,11 @@ msgstr "" msgid "ID" msgstr "" +#. module: website_event_custom_registration +#: model:ir.model.fields,field_description:website_event_custom_registration.field_event_event__keep_custom_text_when_closed +msgid "Keep Custom Text When Closed" +msgstr "" + #. module: website_event_custom_registration #: model:ir.model.fields,help:website_event_custom_registration.field_event_event__external_button_text msgid "Label of the button that links to the external ticketing portal." @@ -88,6 +93,15 @@ msgstr "" msgid "Native ticketing" msgstr "" +#. module: website_event_custom_registration +#: model:ir.model.fields,help:website_event_custom_registration.field_event_event__keep_custom_text_when_closed +msgid "" +"Once registrations close (the event has ended, is sold out or was " +"cancelled), Odoo normally shows its standard 'Registrations Closed' notice." +" Enable this to keep showing this event's custom content instead: the free " +"event message or the external ticket button." +msgstr "" + #. module: website_event_custom_registration #: model_terms:ir.ui.view,arch_db:website_event_custom_registration.view_event_form msgid "Registration" diff --git a/website_event_custom_registration/models/event_event.py b/website_event_custom_registration/models/event_event.py index 278770e5f..feac36140 100644 --- a/website_event_custom_registration/models/event_event.py +++ b/website_event_custom_registration/models/event_event.py @@ -39,3 +39,11 @@ class EventEvent(models.Model): default="Get Tickets", help="Label of the button that links to the external ticketing portal.", ) + keep_custom_text_when_closed = fields.Boolean( + default=False, + help="Once registrations close (the event has ended, is sold out or " + "was cancelled), Odoo normally shows its standard 'Registrations " + "Closed' notice. Enable this to keep showing this event's custom " + "content instead: the free event message or the external ticket " + "button.", + ) diff --git a/website_event_custom_registration/readme/CONFIGURE.md b/website_event_custom_registration/readme/CONFIGURE.md index ae10a9295..800dd1f92 100644 --- a/website_event_custom_registration/readme/CONFIGURE.md +++ b/website_event_custom_registration/readme/CONFIGURE.md @@ -8,4 +8,8 @@ To configure an event: to visitors. - **External registration**: fill in the **External Ticket URL** and, optionally, the **External Button Text** (defaults to `Get Tickets`). -3. Save and publish the event. +3. For *free* and *external* events, optionally enable **Keep Custom Text + When Closed** to keep the custom message or button visible after + registrations close (past, sold-out or cancelled event), instead of + Odoo's standard "Registrations Closed" notice. +4. Save and publish the event. diff --git a/website_event_custom_registration/readme/DESCRIPTION.md b/website_event_custom_registration/readme/DESCRIPTION.md index a4012ec1a..95f982868 100644 --- a/website_event_custom_registration/readme/DESCRIPTION.md +++ b/website_event_custom_registration/readme/DESCRIPTION.md @@ -12,8 +12,12 @@ The three behaviours are combined into one Selection field to avoid the UI state clashes and XPath conflicts that separate modules would create. When an event is *free* or *external*, the native "Sold Out" / -"Registrations Closed" labels and the navbar register link are suppressed, -so the page never looks broken just because no native ticket is configured. +"Registrations Closed" labels and the navbar register link are suppressed +while registrations are open, so the page never looks broken just because +no native ticket is configured. Once registrations close (the event has +ended, is sold out or was cancelled), Odoo's standard "Registrations +Closed" notice takes over by default; a per-event **Keep Custom Text When +Closed** toggle keeps the custom message or button visible instead. When a non-native mode is selected, the backend event form also locks the native seat limit read-only and hides the registration statistics and diff --git a/website_event_custom_registration/readme/USAGE.md b/website_event_custom_registration/readme/USAGE.md index 3c28987e0..b0dc53bb5 100644 --- a/website_event_custom_registration/readme/USAGE.md +++ b/website_event_custom_registration/readme/USAGE.md @@ -7,6 +7,11 @@ mode: - **External registration**: a *Get Tickets* button linking to the external portal, opening in a new tab. +Once registrations close (the event has ended, is sold out or was +cancelled), free and external events show Odoo's standard "Registrations +Closed" notice like native ones, unless **Keep Custom Text When Closed** is +enabled on the event — then the custom message or button stays visible. + The routing applies everywhere the core registration block is rendered: the event page's main call to action, the (desktop and mobile) sidebar, and the event sub-menu, so the experience stays consistent across the site. diff --git a/website_event_custom_registration/static/description/index.html b/website_event_custom_registration/static/description/index.html index a9dbd1359..1f796bc3d 100644 --- a/website_event_custom_registration/static/description/index.html +++ b/website_event_custom_registration/static/description/index.html @@ -390,8 +390,12 @@

Website Event Custom Registration

UI state clashes and XPath conflicts that separate modules would create.

When an event is free or external, the native “Sold Out” / “Registrations Closed” labels and the navbar register link are -suppressed, so the page never looks broken just because no native ticket -is configured.

+suppressed while registrations are open, so the page never looks broken +just because no native ticket is configured. Once registrations close +(the event has ended, is sold out or was cancelled), Odoo’s standard +“Registrations Closed” notice takes over by default; a per-event Keep +Custom Text When Closed toggle keeps the custom message or button +visible instead.

When a non-native mode is selected, the backend event form also locks the native seat limit read-only and hides the registration statistics and attendees smart buttons, since no native registrations are @@ -425,6 +429,10 @@

Configuration

Get Tickets). +
  • For free and external events, optionally enable Keep Custom +Text When Closed to keep the custom message or button visible after +registrations close (past, sold-out or cancelled event), instead of +Odoo’s standard “Registrations Closed” notice.
  • Save and publish the event.
  • @@ -439,6 +447,11 @@

    Usage

  • External registration: a Get Tickets button linking to the external portal, opening in a new tab.
  • +

    Once registrations close (the event has ended, is sold out or was +cancelled), free and external events show Odoo’s standard “Registrations +Closed” notice like native ones, unless Keep Custom Text When Closed +is enabled on the event — then the custom message or button stays +visible.

    The routing applies everywhere the core registration block is rendered: the event page’s main call to action, the (desktop and mobile) sidebar, and the event sub-menu, so the experience stays consistent across the diff --git a/website_event_custom_registration/tests/test_event_custom_registration.py b/website_event_custom_registration/tests/test_event_custom_registration.py index 1a4f07396..bcb89d8f6 100644 --- a/website_event_custom_registration/tests/test_event_custom_registration.py +++ b/website_event_custom_registration/tests/test_event_custom_registration.py @@ -64,3 +64,50 @@ def test_native_mode_does_not_leak_custom_markup(self): self.event.registration_mode = "native" html = self._render_registration() self.assertNotIn("o_wevent_free_registration", html) + + def test_closed_event_defaults_to_core_closed_notice(self): + # Once the event is over, core's "Registrations Closed" notice + # takes over from the custom content by default. + self.event.write( + { + "date_begin": "2020-01-01 20:00:00", + "date_end": "2020-01-02 02:00:00", + "registration_mode": "free", + "free_event_text": "

    Free entry, just turn up

    ", + } + ) + self.assertFalse(self.event.event_registrations_open) + html = self._render_registration() + self.assertNotIn("Free entry, just turn up", html) + self.assertIn("Closed", html) + + def test_closed_event_keeps_custom_text_when_toggled(self): + self.event.write( + { + "date_begin": "2020-01-01 20:00:00", + "date_end": "2020-01-02 02:00:00", + "registration_mode": "free", + "free_event_text": "

    Free entry, just turn up

    ", + "keep_custom_text_when_closed": True, + } + ) + html = self._render_registration() + self.assertIn("Free entry, just turn up", html) + self.assertNotIn("Closed", html) + + def test_closed_external_event_follows_toggle(self): + self.event.write( + { + "date_begin": "2020-01-01 20:00:00", + "date_end": "2020-01-02 02:00:00", + "registration_mode": "external", + "external_ticket_url": "https://tickets.example.com/dj-night", + } + ) + html = self._render_registration() + self.assertNotIn("https://tickets.example.com/dj-night", html) + self.assertIn("Closed", html) + self.event.keep_custom_text_when_closed = True + html = self._render_registration() + self.assertIn("https://tickets.example.com/dj-night", html) + self.assertNotIn("Closed", html) diff --git a/website_event_custom_registration/views/event_event_views.xml b/website_event_custom_registration/views/event_event_views.xml index 4b55b9be4..26cdba0d4 100644 --- a/website_event_custom_registration/views/event_event_views.xml +++ b/website_event_custom_registration/views/event_event_views.xml @@ -34,6 +34,13 @@ invisible="registration_mode != 'external'" required="registration_mode == 'external'" /> + + diff --git a/website_event_custom_registration/views/website_event_templates.xml b/website_event_custom_registration/views/website_event_templates.xml index 8463ca76e..aeb0fb118 100644 --- a/website_event_custom_registration/views/website_event_templates.xml +++ b/website_event_custom_registration/views/website_event_templates.xml @@ -20,11 +20,18 @@ inherit_id="website_event.registration_template" name="Event Registration: custom modes (free / external)" > - + +
    @@ -32,7 +39,7 @@
    + always in native mode; for free and external events it is + suppressed while registrations are open (it would wrongly flag + a missing native ticket) and takes over once registrations + close — unless the event keeps its custom text via + keep_custom_text_when_closed. --> not event.event_registrations_open and event.registration_mode == 'native' + >not event.event_registrations_open and (event.registration_mode == 'native' or not event.keep_custom_text_when_closed)