[REM] website_event_online: merge into website_event

PURPOSE

Clean organization and models linked to Event Online feature introduced in
semi stable saas-13.3 at odoo/odoo@981f95bbf4 and odoo/enterprise@14722028ae

Also clean website event menu not being available in website event but only
in website event track, which implies some extra-code to manage it.

In short: merge website_event_online in website_event, website_event_track
_(online/session) in website_event_track.

RATIONALE

_online modules have been added to extend content of website_event and
website_event_track without having any impact on those module. First step
of cleaning is to move this content directly in base module.

track_session module is mainly a rewrite of track module. Second step of
cleaning is to move its content directly in website_event_track.

SPECIFICATIONS

Move remaining content of website event online to website event.

Update dependencies accordingly.

LINKS

Task ID-2319779
COM odoo/odoo#56067
ENT odoo/enterprise#12520
UPG odoo/upgrade#1654
This commit is contained in:
Thibault Delavallée
2020-08-21 17:30:13 +00:00
parent d697966585
commit b0845c0f2b
50 changed files with 249 additions and 429 deletions
-1
View File
@@ -16,7 +16,6 @@ automatic lead generation, full Online support, ...
'event_sale',
'website_event_crm_questions',
'website_event_questions',
'website_event_online',
'website_event_questions',
'website_event_meet',
'website_event_sale',
+1 -1
View File
@@ -7,7 +7,7 @@ from odoo.addons.base.tests.common import HttpCaseWithUserDemo, HttpCaseWithUser
from odoo.addons.event_crm.tests.common import TestEventCrmCommon
from odoo.addons.sales_team.tests.common import TestSalesCommon
from odoo.addons.website.tests.test_website_visitor import MockVisitor
from odoo.addons.website_event_online.tests.common import EventDtPatcher
from odoo.addons.website_event.tests.common import EventDtPatcher
class TestEventFullCommon(TestEventCrmCommon, TestSalesCommon, EventDtPatcher, MockVisitor):
+5 -1
View File
@@ -27,15 +27,19 @@
'views/event_templates_widgets.xml',
'views/website_templates.xml',
'views/event_event_views.xml',
'views/event_registration_views.xml',
'views/event_type_views.xml',
'views/website_event_menu_views.xml',
'views/website_visitor_views.xml',
'views/event_menus.xml',
'security/ir.model.access.csv',
'security/event_security.xml',
],
'demo': [
'data/res_partner_demo.xml',
'data/event_demo.xml'
'data/website_visitor_demo.xml',
'data/event_demo.xml',
'data/event_registration_demo.xml',
],
'application': True,
}
+22 -1
View File
@@ -345,13 +345,34 @@ class WebsiteEventController(http.Controller):
return list(registrations.values())
def _create_attendees_from_registration_post(self, event, registration_data):
""" Also try to set a visitor (from request) and
a partner (if visitor linked to a user for example). Purpose is to gather
as much informations as possible, notably to ease future communications.
Also try to update visitor informations based on registration info. """
visitor_sudo = request.env['website.visitor']._get_visitor_from_request(force_create=True)
visitor_sudo._update_visitor_last_visit()
visitor_values = {}
registrations_to_create = []
for registration_values in registration_data:
registration_values['event_id'] = event.id
if not registration_values.get('partner_id'):
if not registration_values.get('partner_id') and visitor_sudo.partner_id:
registration_values['partner_id'] = visitor_sudo.partner_id.id
elif not registration_values.get('partner_id'):
registration_values['partner_id'] = request.env.user.partner_id.id
if visitor_sudo:
# registration may give a name to the visitor, yay
if registration_values.get('name') and not visitor_sudo.name and not visitor_values.get('name'):
visitor_values['name'] = registration_values['name']
# update registration based on visitor
registration_values['visitor_id'] = visitor_sudo.id
registrations_to_create.append(registration_values)
if visitor_values:
visitor_sudo.write(visitor_values)
return request.env['event.registration'].sudo().create(registrations_to_create)
@http.route(['''/event/<model("event.event"):event>/registration/confirm'''], type='http', auth="public", methods=['POST'], website=True)
+2
View File
@@ -41,6 +41,7 @@
<record id="event.event_1" model="event.event">
<field name="website_menu" eval="True"/>
<field name="website_published" eval="True"/>
<field name="menu_register_cta" eval="True"/>
<field name="subtitle">The Great Reno Balloon Race is the world's largest free hot-air ballooning event.</field>
<field name="cover_properties">{"background-image": "url('/website_event/static/src/img/event_cover_1.jpg')", "resize_class": "o_record_has_cover o_half_screen_height", "opacity": "0.4"}</field>
<field name="description" type="html">
@@ -151,6 +152,7 @@
<record id="event.event_7" model="event.event">
<field name="website_menu" eval="True"/>
<field name="website_published" eval="True"/>
<field name="menu_register_cta" eval="True"/>
<field name="subtitle">Our newest collection will be revealed online! Interact with us on our live streams!</field>
<field name="cover_properties">{"background-image": "url('/website_event/static/src/img/event_cover_7.jpg')", "resize_class": "o_record_has_cover o_half_screen_height", "opacity": "0.4"}</field>
<field name="description" type="html">
@@ -3,16 +3,16 @@
<!-- OpenWood Collection Online Reveal: Gemini (all) -->
<record id="event.event_registration_7_0" model="event.registration">
<field name="visitor_id" ref="website_event_online.website_visitor_event_0"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
</record>
<record id="event.event_registration_7_1" model="event.registration">
<field name="visitor_id" ref="website_event_online.website_visitor_event_0"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
</record>
<record id="event.event_registration_7_2" model="event.registration">
<field name="visitor_id" ref="website_event_online.website_visitor_event_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
</record>
<record id="event.event_registration_7_3" model="event.registration">
<field name="visitor_id" ref="website_event_online.website_visitor_event_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
</record>
</data></odoo>
@@ -18,7 +18,7 @@
</record>
<record id="website_visitor_event_2_1" model="website.visitor">
<field name="name">Philipe J. Fry (old)</field>
<field name="parent_id" ref="website_event_online.website_visitor_event_2"/>
<field name="parent_id" ref="website_event.website_visitor_event_2"/>
<field name="partner_id" eval="False"/>
<field name="country_id" ref="base.be"/>
</record>
+3
View File
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event_event
from . import event_registration
from . import event_type
from . import website
from . import website_event_menu
from . import website_menu
from . import website_visitor
+81 -7
View File
@@ -4,8 +4,11 @@
import json
import werkzeug.urls
from pytz import utc
from odoo import api, fields, models, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.osv import expression
GOOGLE_CALENDAR_URL = 'https://www.google.com/calendar/render?'
@@ -36,6 +39,9 @@ class Event(models.Model):
help="Creates menus Introduction, Location and Register on the page "
"of the event on the website.")
menu_id = fields.Many2one('website.menu', 'Event Menu', copy=False)
menu_register_cta = fields.Boolean(
'Add Register Button', compute='_compute_menu_register_cta',
readonly=False, store=True)
community_menu = fields.Boolean(
"Community Menu", compute="_compute_community_menu",
readonly=False, store=True,
@@ -43,16 +49,58 @@ class Event(models.Model):
community_menu_ids = fields.One2many(
"website.event.menu", "event_id", string="Event Community Menus",
domain=[("menu_type", "=", "community")])
# live information
is_ongoing = fields.Boolean(
'Is Ongoing', compute='_compute_time_data', search='_search_is_ongoing',
help="Whether event has begun")
is_done = fields.Boolean(
'Is Done', compute='_compute_time_data',
help="Whether event is finished")
start_today = fields.Boolean(
'Start Today', compute='_compute_time_data',
help="Whether event is going to start today if still not ongoing")
start_remaining = fields.Integer(
'Remaining before start', compute='_compute_time_data',
help="Remaining time before event starts (minutes)")
def _compute_is_participating(self):
# we don't allow public user to see participating label
if self.env.user != self.env['website'].get_current_website().user_id:
email = self.env.user.partner_id.email
for event in self:
domain = ['&', '|', ('email', '=', email), ('partner_id', '=', self.env.user.partner_id.id), ('event_id', '=', event.id)]
event.is_participating = self.env['event.registration'].sudo().search_count(domain)
"""Heuristic
* public, no visitor: not participating as we have no information;
* public and visitor: check visitor is linked to a registration. As
visitors are merged on the top parent, current visitor check is
sufficient even for successive visits;
* logged, no visitor: check partner is linked to a registration. Do
not check the email as it is not really secure;
* logged as visitor: check partner or visitor are linked to a
registration;
"""
current_visitor = self.env['website.visitor']._get_visitor_from_request(force_create=False)
if self.env.user._is_public() and not current_visitor:
events = self.env['event.event']
elif self.env.user._is_public():
events = self.env['event.registration'].sudo().search([
('event_id', 'in', self.ids),
('visitor_id', '=', current_visitor.id),
]).event_id
else:
self.is_participating = False
if current_visitor:
domain = [
'|',
('partner_id', '=', self.env.user.partner_id.id),
('visitor_id', '=', current_visitor.id)
]
else:
domain = [('partner_id', '=', self.env.user.partner_id.id)]
events = self.env['event.registration'].sudo().search(
expression.AND([
domain,
[('event_id', 'in', self.ids)]
])
).event_id
for event in self:
event.is_participating = event in events
@api.depends('event_type_id')
def _compute_website_menu(self):
@@ -75,6 +123,28 @@ class Event(models.Model):
elif not event.website_menu:
event.community_menu = False
@api.depends('website_menu')
def _compute_menu_register_cta(self):
for event in self:
event.menu_register_cta = event.website_menu
@api.depends('date_begin', 'date_end')
def _compute_time_data(self):
""" Compute start and remaining time. Do everything in UTC as we compute only
time deltas here. """
now_utc = utc.localize(fields.Datetime.now().replace(microsecond=0))
for event in self:
date_begin_utc = utc.localize(event.date_begin, is_dst=False)
date_end_utc = utc.localize(event.date_end, is_dst=False)
event.is_ongoing = date_begin_utc <= now_utc <= date_end_utc
event.is_done = now_utc > date_end_utc
event.start_today = date_begin_utc.date() == now_utc.date()
if date_begin_utc >= now_utc:
td = date_begin_utc - now_utc
event.start_remaining = int(td.total_seconds() / 60)
else:
event.start_remaining = 0
@api.depends('name')
def _compute_website_url(self):
super(Event, self)._compute_website_url()
@@ -82,6 +152,10 @@ class Event(models.Model):
if event.id: # avoid to perform a slug on a not yet saved record in case of an onchange.
event.website_url = '/event/%s' % slug(event)
# ------------------------------------------------------------
# CRUD
# ------------------------------------------------------------
@api.model
def create(self, vals):
res = super(Event, self).create(vals)
@@ -12,6 +12,17 @@
<field name="perm_unlink" eval="False"/>
</record>
<record id="ir_rule_event_tag_public" model="ir.rule">
<field name="name">Event Tag: public/portal: color = published</field>
<field name="model_id" ref="event.model_event_tag"/>
<field name="domain_force">['&amp;', ('color', '!=', False), ('color', '!=', 0)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="ir_rule_event_event_ticket_public" model="ir.rule">
<field name="name">Event Ticket: public/portal: published read</field>
<field name="model_id" ref="event.model_event_event_ticket"/>
@@ -8,3 +8,4 @@ access_event_category_public,event.tag.category.public,event.model_event_tag_cat
access_event_tag_public,event.tag.public,event.model_event_tag,,1,0,0,0
access_website_event_menu_public,website.event.menu,model_website_event_menu,,1,0,0,0
access_website_event_menu_manager,website.event.menu,model_website_event_menu,event.group_event_manager,1,1,1,1
website_visitor_access_event_user,website.visitor.access.event.user,model_website_visitor,event.group_event_user,1,1,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
8 access_event_tag_public event.tag.public event.model_event_tag 1 0 0 0
9 access_website_event_menu_public website.event.menu model_website_event_menu 1 0 0 0
10 access_website_event_menu_manager website.event.menu model_website_event_menu event.group_event_manager 1 1 1 1
11 website_visitor_access_event_user website.visitor.access.event.user model_website_visitor event.group_event_user 1 1 0 0
@@ -1,4 +1,4 @@
odoo.define('website_event_online.display_timer_widget', function (require) {
odoo.define('website_event.display_timer_widget', function (require) {
'use strict';
var core = require('web.core');
@@ -1,4 +1,4 @@
odoo.define('website_event_online.register_toaster_widget', function (require) {
odoo.define('website_event.register_toaster_widget', function (require) {
'use strict';
let core = require('web.core');
+1
View File
@@ -4,5 +4,6 @@
from . import common
from . import test_event_internals
from . import test_ui
from . import test_visitor_events
from . import test_website_event
from . import test_website_event_access
+57
View File
@@ -1,8 +1,52 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta, time
from unittest.mock import patch
from odoo.addons.event.tests.common import TestEventCommon
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.fields import Datetime as FieldsDatetime, Date as FieldsDate
from odoo.tests.common import SavepointCase
class EventDtPatcher(SavepointCase):
@classmethod
def setUpClass(cls):
super(EventDtPatcher, cls).setUpClass()
cls.reference_now = datetime(2020, 7, 6, 10, 0, 0)
cls.reference_today = datetime(2020, 7, 6)
cls.event_dt = patch(
'odoo.addons.event.models.event_event.fields.Datetime',
wraps=FieldsDatetime
)
cls.wevent_main_dt = patch(
'odoo.addons.website_event.controllers.main.fields.Datetime',
wraps=FieldsDatetime
)
cls.event_date = patch(
'odoo.addons.event.models.event_event.fields.Date',
wraps=FieldsDate
)
cls.wevent_main_date = patch(
'odoo.addons.website_event.controllers.main.fields.Date',
wraps=FieldsDate
)
cls.mock_event_dt = cls.event_dt.start()
cls.mock_wevent_main_dt = cls.wevent_main_dt.start()
cls.mock_event_date = cls.event_date.start()
cls.mock_wevent_main_date = cls.wevent_main_date.start()
cls.mock_event_dt.now.return_value = cls.reference_now
cls.mock_wevent_main_dt.now.return_value = cls.reference_now
cls.mock_event_date.today.return_value = cls.reference_today
cls.mock_wevent_main_date.today.return_value = cls.reference_today
cls.addClassCleanup(cls.event_dt.stop)
cls.addClassCleanup(cls.wevent_main_dt.stop)
cls.addClassCleanup(cls.event_date.stop)
cls.addClassCleanup(cls.wevent_main_date.stop)
class TestWebsiteEventCommon(TestEventCommon):
@@ -43,3 +87,16 @@ class TestWebsiteEventCommon(TestEventCommon):
# without website.event.menu model (or crappy code based on name)
# else:
# self.assertFalse(bool(view))
class TestEventOnlineCommon(TestEventCommon, EventDtPatcher):
@classmethod
def setUpClass(cls):
super(TestEventOnlineCommon, cls).setUpClass()
# event if 8-18 in Europe/Brussels (DST) (first day: begins at 9, last day: ends at 15)
cls.event_0.write({
'date_begin': datetime.combine(cls.reference_now, time(7, 0)) - timedelta(days=1),
'date_end': datetime.combine(cls.reference_now, time(13, 0)) + timedelta(days=1),
})
+3
View File
@@ -10,8 +10,11 @@
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Event Assets Frontend">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/website_event/static/src/scss/website_event.scss"/>
<link rel="stylesheet" type="text/scss" href="/website_event/static/src/scss/website_event_templates.scss"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/website_event/static/src/js/display_timer_widget.js"></script>
<script type="text/javascript" src="/website_event/static/src/js/register_toaster_widget.js"></script>
<script type="text/javascript" src="/website_event/static/src/js/website_geolocation.js"></script>
<script type="text/javascript" src="/website_event/static/src/js/website_event.js"></script>
<script type="text/javascript" src="/website_event/static/src/js/website_event_ticket_details.js"></script>
@@ -23,6 +23,8 @@
<div name="event_menu_configuration" groups="base.group_no_one">
<label for="website_menu" string="Website Submenu"/>
<field name="website_menu"/>
<label for="menu_register_cta" string="Register Button"/>
<field name="menu_register_cta"/>
<label for="community_menu" string="Community"/>
<field name="community_menu"/>
</div>
@@ -97,15 +97,16 @@
<template id="event_category_tag" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Tags">
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
<t t-foreach="categories" t-as="category">
<li t-if="category.tag_ids" class="nav-item dropdown mr-2 my-1">
<li t-if="category.tag_ids and any(tag.color for tag in category.tag_ids)" class="nav-item dropdown mr-2 my-1">
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-folder-open"/>
<t t-esc="category.name"/>
</a>
<div class="dropdown-menu">
<t t-foreach="category.tag_ids" t-as="tag">
<a t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
<a t-if="tag.color"
t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
<t t-esc="tag.name"/>
</a>
</t>
@@ -205,7 +206,8 @@
<div itemprop="location" t-field="event.address_id" t-options="{'widget': 'contact', 'fields': ['city'], 'no_marker': 'true'}"/>
<div class="mt8 d-flex align-items-center">
<t t-foreach="event.tag_ids" t-as="tag">
<span t-attf-class="badge mr8 #{'badge-primary' if tag in search_tags else 'badge-light'}">
<span t-if="tag.color"
t-attf-class="badge mr8 #{'badge-primary' if tag in search_tags else 'badge-light'} #{'o_tag_color_%s' % tag.color if tag.color else ''}">
<span t-esc="tag.name"/>
</span>
</t>
@@ -52,6 +52,12 @@
</t>
</t>
</ul>
<!-- Add Register additional CTA button, in addition to menus -->
<a t-if="event.menu_register_cta and not event.is_participating"
t-att-href="'/event/%s/register' % (slug(event))"
class="btn btn-primary ml-auto">
Register
</a>
</div>
</div>
</nav>
@@ -121,6 +121,7 @@
<i class="fa fa-pencil mr-2" role="img" aria-label="Edit" title="Edit event registration in backend"/><em>Configure Event Tickets</em>
</a>
</div>
<div t-if="toast_message" class="o_wevent_register_toaster d-none" t-att-data-message="toast_message"/>
<div t-if="not event.event_registrations_open" class="bg-white mb-5">
<div class="alert alert-info mb-0 d-flex justify-content-between align-items-center" role="status">
<t t-if="event.start_sale_date and event.start_sale_date &gt; datetime.date.today()">
@@ -52,4 +52,30 @@
</div>
</template>
<!-- Timer widget -->
<template id="display_timer_widget" name="Display Timer Widget">
<t t-set="pre_countdown_display" t-value="bool(pre_countdown_text) or pre_countdown_display"/>
<t t-set="pre_countdown_time" t-value="datetime.datetime.now().timestamp() + int(pre_remaining_time)"/>
<div class="o_display_timer"
t-att-data-display-class="display_class"
t-att-data-main-countdown-time="datetime.datetime.now().timestamp() + int(main_remaining_time)"
t-att-data-main-countdown-text="main_countdown_text"
t-att-data-main-countdown-display="main_countdown_display"
t-att-data-pre-countdown-time="pre_countdown_time"
t-att-data-pre-countdown-display="pre_countdown_display"
t-att-data-pre-countdown-text="pre_countdown_text">
<t t-set="remaining_time" t-value="pre_remaining_time if pre_remaining_time else main_remaining_time"/>
<span class="o_display_timer_countdown d-flex justify-content-center">
<span class="o_countdown_text pr-1" t-esc="pre_countdown_text if pre_countdown_text else main_countdown_text if not pre_countdown_display else ''"/>
<div t-if="int(remaining_time) > 86400"
class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_days pr-1">0</span><span class="o_countdown_metric pr-1">days</span></div>
<div t-if="int(remaining_time) > 3600"
class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_hours">00</span><span class="o_countdown_metric">:</span></div>
<div class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_minutes">00</span><span class="o_countdown_metric">:</span></div>
<div class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_seconds">00</span><span class="o_countdown_metric"></span></div>
</span>
</div>
</template>
</odoo>
@@ -29,7 +29,7 @@
<field name="inherit_id" ref="website.website_visitor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@id='w_visitor_visit_counter']" position="before">
<button name="%(website_event_online.event_registration_action_from_visitor)d"
<button name="%(website_event.event_registration_action_from_visitor)d"
type="action"
class="oe_stat_button" icon="fa-ticket"
attrs="{'invisible': [('event_registration_count', '=', 0)]}">
+1 -5
View File
@@ -11,11 +11,7 @@
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
# STD-TODO: remove 'website_event_track_online' we just need it to be
# able to use 'website.event.menu' which is defined in 'website_event_track'
# as well as even form view defined in track_online
'website_event_online',
'website_event_track_online',
'website_event',
'website_jitsi',
],
'demo': ['data/website_event_meet_demo.xml'],
-6
View File
@@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models
from . import tests
@@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Events Online',
'category': 'Marketing/Events',
'sequence': 1001,
'version': '1.0',
'summary': 'Bridge module to support Online Events',
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
'website_event'
],
'data': [
'security/ir.model.access.csv',
'security/event_security.xml',
'views/assets.xml',
'views/event_event_views.xml',
'views/event_registration_views.xml',
'views/website_visitor_views.xml',
'views/event_templates_event.xml',
'views/event_templates_widgets.xml',
],
'demo': [
'data/website_visitor_demo.xml',
'data/event_demo.xml',
'data/event_registration_demo.xml',
],
'application': False,
'installable': True,
}
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event_registration
@@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.website_event.controllers.main import WebsiteEventController
from odoo.http import request
class WebsiteEventOnlineController(WebsiteEventController):
def _create_attendees_from_registration_post(self, event, registration_data):
""" Override registration data to try to set a visitor (from request) and
a partner (if visitor linked to a user for example). Purpose is to gather
as much informations as possible, notably to ease future communications.
Also try to update visitor informations based on registration info. """
visitor_sudo = request.env['website.visitor']._get_visitor_from_request(force_create=True)
visitor_sudo._update_visitor_last_visit()
if visitor_sudo:
visitor_values = {}
for info in registration_data:
# registration may give a name to the visitor, yay
if info.get('name') and not visitor_sudo.name and not visitor_values.get('name'):
visitor_values['name'] = info['name']
# update registration based on visitor
info['visitor_id'] = visitor_sudo.id
if not info.get('partner_id') and visitor_sudo.partner_id:
info['partner_id'] = visitor_sudo.partner_id.id
if visitor_values:
visitor_sudo.write(visitor_values)
return super(WebsiteEventOnlineController, self)._create_attendees_from_registration_post(event, registration_data)
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event.event_1" model="event.event">
<field name="menu_register_cta" eval="True"/>
</record>
<record id="event.event_7" model="event.event">
<field name="menu_register_cta" eval="True"/>
</record>
</odoo>
@@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event_event
from . import event_registration
from . import website_visitor
@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from pytz import timezone, utc
from odoo import api, fields, models, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.resource.models.resource import float_to_time
from odoo.osv import expression
class Event(models.Model):
_name = 'event.event'
_inherit = 'event.event'
# frontend options
menu_register_cta = fields.Boolean(
'Add Register Button', compute='_compute_menu_register_cta',
readonly=False, store=True)
# live information
is_ongoing = fields.Boolean(
'Is Ongoing', compute='_compute_time_data', search='_search_is_ongoing',
help="Whether event has begun")
is_done = fields.Boolean(
'Is Done', compute='_compute_time_data',
help="Whether event is finished")
start_today = fields.Boolean(
'Start Today', compute='_compute_time_data',
help="Whether event is going to start today if still not ongoing")
start_remaining = fields.Integer(
'Remaining before start', compute='_compute_time_data',
help="Remaining time before event starts (minutes)")
@api.depends('website_menu')
def _compute_menu_register_cta(self):
for event in self:
event.menu_register_cta = event.website_menu
@api.depends('date_begin', 'date_end')
def _compute_time_data(self):
""" Compute start and remaining time. Do everything in UTC as we compute only
time deltas here. """
now_utc = utc.localize(fields.Datetime.now().replace(microsecond=0))
for event in self:
date_begin_utc = utc.localize(event.date_begin, is_dst=False)
date_end_utc = utc.localize(event.date_end, is_dst=False)
event.is_ongoing = date_begin_utc <= now_utc <= date_end_utc
event.is_done = now_utc > date_end_utc
event.start_today = date_begin_utc.date() == now_utc.date()
if date_begin_utc >= now_utc:
td = date_begin_utc - now_utc
event.start_remaining = int(td.total_seconds() / 60)
else:
event.start_remaining = 0
def _compute_is_participating(self):
""" Override is_participating to improve heuristic that is now
* public, no visitor: not participating as we have no information;
* public and visitor: check visitor is linked to a registration. As
visitors are merged on the top parent, current visitor check is
sufficient even for successive visits;
* logged, no visitor: check partner is linked to a registration. Do
not check the email as it is not really secure;
* logged as visitor: check partner or visitor are linked to a
registration;
"""
current_visitor = self.env['website.visitor']._get_visitor_from_request(force_create=False)
if self.env.user._is_public() and not current_visitor:
events = self.env['event.event']
elif self.env.user._is_public():
events = self.env['event.registration'].sudo().search([
('event_id', 'in', self.ids),
('visitor_id', '=', current_visitor.id),
]).event_id
else:
if current_visitor:
domain = [
'|',
('partner_id', '=', self.env.user.partner_id.id),
('visitor_id', '=', current_visitor.id)
]
else:
domain = [('partner_id', '=', self.env.user.partner_id.id)]
events = self.env['event.registration'].sudo().search(
expression.AND([
domain,
[('event_id', 'in', self.ids)]
])
).event_id
for event in self:
event.is_participating = event in events
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<record id="ir_rule_event_tag_public" model="ir.rule">
<field name="name">Event Tag: public/portal: color = published</field>
<field name="model_id" ref="event.model_event_tag"/>
<field name="domain_force">['&amp;', ('color', '!=', False), ('color', '!=', 0)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
</data></odoo>
@@ -1,2 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
website_visitor_access_event_user,website.visitor.access.event.user,model_website_visitor,event.group_event_user,1,1,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 website_visitor_access_event_user website.visitor.access.event.user model_website_visitor event.group_event_user 1 1 0 0
@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
from . import test_visitor_events
@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta, time
from unittest.mock import patch
from odoo.addons.event.tests.common import TestEventCommon
from odoo.fields import Datetime as FieldsDatetime, Date as FieldsDate
from odoo.tests.common import SavepointCase
class EventDtPatcher(SavepointCase):
@classmethod
def setUpClass(cls):
super(EventDtPatcher, cls).setUpClass()
cls.reference_now = datetime(2020, 7, 6, 10, 0, 0)
cls.reference_today = datetime(2020, 7, 6)
cls.event_dt = patch(
'odoo.addons.event.models.event_event.fields.Datetime',
wraps=FieldsDatetime
)
cls.wevent_main_dt = patch(
'odoo.addons.website_event.controllers.main.fields.Datetime',
wraps=FieldsDatetime
)
cls.event_online_dt = patch(
'odoo.addons.website_event_online.models.event_event.fields.Datetime',
wraps=FieldsDatetime
)
cls.event_date = patch(
'odoo.addons.event.models.event_event.fields.Date',
wraps=FieldsDate
)
cls.wevent_main_date = patch(
'odoo.addons.website_event.controllers.main.fields.Date',
wraps=FieldsDate
)
cls.mock_event_dt = cls.event_dt.start()
cls.mock_wevent_main_dt = cls.wevent_main_dt.start()
cls.mock_event_online_dt = cls.event_online_dt.start()
cls.mock_event_date = cls.event_date.start()
cls.mock_wevent_main_date = cls.wevent_main_date.start()
cls.mock_event_dt.now.return_value = cls.reference_now
cls.mock_wevent_main_dt.now.return_value = cls.reference_now
cls.mock_event_online_dt.now.return_value = cls.reference_now
cls.mock_event_date.today.return_value = cls.reference_today
cls.mock_wevent_main_date.today.return_value = cls.reference_today
cls.addClassCleanup(cls.event_dt.stop)
cls.addClassCleanup(cls.wevent_main_dt.stop)
cls.addClassCleanup(cls.event_online_dt.stop)
cls.addClassCleanup(cls.event_date.stop)
cls.addClassCleanup(cls.wevent_main_date.stop)
class TestEventOnlineCommon(TestEventCommon, EventDtPatcher):
@classmethod
def setUpClass(cls):
super(TestEventOnlineCommon, cls).setUpClass()
# event if 8-18 in Europe/Brussels (DST) (first day: begins at 9, last day: ends at 15)
cls.event_0.write({
'date_begin': datetime.combine(cls.reference_now, time(7, 0)) - timedelta(days=1),
'date_end': datetime.combine(cls.reference_now, time(13, 0)) + timedelta(days=1),
})
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Event Online Assets Frontend">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/website_event_online/static/src/scss/website_event_templates.scss"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/website_event_online/static/src/js/display_timer_widget.js"></script>
<script type="text/javascript" src="/website_event_online/static/src/js/register_toaster_widget.js"></script>
</xpath>
</template>
</odoo>
@@ -1,18 +0,0 @@
<?xml version="1.0"?>
<odoo><data>
<record id="event_event_view_form" model="ir.ui.view">
<field name="name">event.event.view.form.inherit.online</field>
<field name="model">event.event</field>
<field name="inherit_id" ref="event.view_event_form"/>
<field name="arch" type="xml">
<xpath expr="//[field@name='website_menu']" position="after">
<div name="event_menu_configuration" groups="base.group_no_one">
<label for="menu_register_cta" string="Register Button"/>
<field name="menu_register_cta"/>
</div>
</xpath>
</field>
</record>
</data></odoo>
@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add Register additional CTA button, in addition to menus -->
<template id="event_layout_main" inherit_id="website_event.layout">
<xpath expr="//div[@id='o_wevent_event_submenu']/ul" position="after">
<a t-if="event.menu_register_cta and not event.is_participating"
t-att-href="'/event/%s/register' % (slug(event))"
class="btn btn-primary ml-auto">
Register
</a>
</xpath>
</template>
<!-- Support tag colors -->
<template id="events_list" name="Events List (Online)"
inherit_id="website_event.events_list">
<xpath expr="//t[@t-foreach='event.tag_ids']/span" position="replace">
<span t-if="tag.color" t-attf-class="badge mr8 #{'badge-primary' if tag in search_tags else 'badge-light'} #{'o_tag_color_%s' % tag.color if tag.color else ''}">
<span t-esc="tag.name"/>
</span>
</xpath>
</template>
<template id="event_category_tag" name="Filter by Tags (Online)"
inherit_id="website_event.event_category_tag">
<xpath expr="//t[@t-foreach='categories']" position="replace">
<t t-foreach="categories" t-as="category">
<li t-if="category.tag_ids and any(tag.color for tag in category.tag_ids)" class="nav-item dropdown mr-2 my-1">
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-folder-open"/>
<t t-esc="category.name"/>
</a>
<div class="dropdown-menu">
<t t-foreach="category.tag_ids" t-as="tag">
<a t-if="tag.color"
t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
<t t-esc="tag.name"/>
</a>
</t>
</div>
</li>
</t>
</xpath>
</template>
<template id="registration_template" inherit_id="website_event.registration_template">
<xpath expr="//div" position="after">
<div t-if="toast_message" class="o_wevent_register_toaster d-none" t-att-data-message="toast_message"/>
</xpath>
</template>
</odoo>
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="display_timer_widget" name="Display Timer Widget">
<t t-set="pre_countdown_display" t-value="bool(pre_countdown_text) or pre_countdown_display"/>
<t t-set="pre_countdown_time" t-value="datetime.datetime.now().timestamp() + int(pre_remaining_time)"/>
<div class="o_display_timer"
t-att-data-display-class="display_class"
t-att-data-main-countdown-time="datetime.datetime.now().timestamp() + int(main_remaining_time)"
t-att-data-main-countdown-text="main_countdown_text"
t-att-data-main-countdown-display="main_countdown_display"
t-att-data-pre-countdown-time="pre_countdown_time"
t-att-data-pre-countdown-display="pre_countdown_display"
t-att-data-pre-countdown-text="pre_countdown_text">
<t t-set="remaining_time" t-value="pre_remaining_time if pre_remaining_time else main_remaining_time"/>
<span class="o_display_timer_countdown d-flex justify-content-center">
<span class="o_countdown_text pr-1" t-esc="pre_countdown_text if pre_countdown_text else main_countdown_text if not pre_countdown_display else ''"/>
<div t-if="int(remaining_time) > 86400"
class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_days pr-1">0</span><span class="o_countdown_metric pr-1">days</span></div>
<div t-if="int(remaining_time) > 3600"
class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_hours">00</span><span class="o_countdown_metric">:</span></div>
<div class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_minutes">00</span><span class="o_countdown_metric">:</span></div>
<div class="o_countdown_metric_container"><span class="o_countdown_remaining o_timer_seconds">00</span><span class="o_countdown_metric"></span></div>
</span>
</div>
</template>
</odoo>
@@ -12,7 +12,7 @@
'description': "",
'depends': [
'sms',
'website_event_online',
'website_event',
'website_event_track',
],
'data': [
@@ -3,12 +3,12 @@
<record id="event_7_track_2_visitor_0" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website_event_online.website_visitor_event_0"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="is_wishlisted" eval="True"/>
</record>
<record id="event_7_track_2_visitor_2" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website_event_online.website_visitor_event_2"/>
<field name="visitor_id" ref="website_event.website_visitor_event_2"/>
<field name="is_wishlisted" eval="True"/>
</record>
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.website_event_online.tests.common import TestEventOnlineCommon
from odoo.addons.website_event.tests.common import TestEventOnlineCommon
class TestEventTrackOnlineCommon(TestEventOnlineCommon):
@@ -4,7 +4,7 @@
<record id="event_event_view_form" model="ir.ui.view">
<field name="name">event.event.view.from.inherit.track.online</field>
<field name="model">event.event</field>
<field name="inherit_id" ref="website_event_online.event_event_view_form"/>
<field name="inherit_id" ref="website_event.event_event_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='website_menu']" position="after">
<label for="website_track" string="Showcase Tracks"/>
@@ -4,7 +4,7 @@
<record id="website_visitor_view_tree" model="ir.ui.view">
<field name="name">website.visitor.view.tree.inherit.event.track</field>
<field name="model">website.visitor</field>
<field name="inherit_id" ref="website_event_online.website_visitor_view_tree"/>
<field name="inherit_id" ref="website_event.website_visitor_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='event_registration_count']" position="after">
<field name="event_track_wishlisted_count"/>
@@ -15,7 +15,7 @@
<record id="website_visitor_view_form" model="ir.ui.view">
<field name="name">website.visitor.view.form.inherit.event.track</field>
<field name="model">website.visitor</field>
<field name="inherit_id" ref="website_event_online.website_visitor_view_form"/>
<field name="inherit_id" ref="website_event.website_visitor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@id='w_visitor_visit_counter']" position="before">
<button name="%(website_event_track_online.event_track_action_from_visitor)d"
@@ -31,7 +31,7 @@
<record id="website_visitor_view_kanban" model="ir.ui.view">
<field name="name">website.visitor.view.kanban.inherit.event.track</field>
<field name="model">website.visitor</field>
<field name="inherit_id" ref="website_event_online.website_visitor_view_kanban"/>
<field name="inherit_id" ref="website_event.website_visitor_view_kanban"/>
<field name="arch" type="xml">
<field name="page_ids" position="after">
<field name="event_track_wishlisted_count"/>
@@ -141,21 +141,21 @@
<record id="event_track_visitor_admin_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event_online.website_visitor_event_0"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">3</field>
</record>
<record id="event_track_visitor_demo_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event_online.website_visitor_event_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">2</field>
</record>
<record id="event_track_visitor_portal_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event_online.website_visitor_event_2"/>
<field name="visitor_id" ref="website_event.website_visitor_event_2"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">1</field>
</record>
@@ -140,7 +140,7 @@
<div class="mx-2" t-if="track.website_cta">
<t t-set="cta_coundown" t-value="bool(track.website_cta_start_remaining)"/>
<div t-if="cta_coundown" class="text-center mt-2 mt-md-0 w-100 btn btn-primary d-none">
<t t-call="website_event_online.display_timer_widget">
<t t-call="website_event.display_timer_widget">
<t t-set="pre_remaining_time" t-value="int(track.track_start_remaining)"/>
<!--<t t-set="pre_countdown_text" t-value="'Talk starts in'"/>-->
<t t-set="pre_countdown_display" t-value="bool(False)"/>