[REM] website_event_track_online: merge into website_event_track

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 track online to website event track.
Agenda notably is completely replaced by the new one developed within
track_online module.

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 b0845c0f2b
commit 1163013ed6
91 changed files with 1207 additions and 2000 deletions
@@ -111,7 +111,7 @@ var LivechatButton = Widget.extend({
this.$el.css('color', this.options.button_text_color);
}
// If website_event_track_online installed, put the livechat banner above the PWA banner.
// If website_event_track installed, put the livechat banner above the PWA banner.
var pwaBannerHeight = $('.o_pwa_install_banner').outerHeight(true);
if (pwaBannerHeight) {
this.$el.css('bottom', pwaBannerHeight + 'px');
-1
View File
@@ -20,7 +20,6 @@ automatic lead generation, full Online support, ...
'website_event_meet',
'website_event_sale',
'website_event_track',
'website_event_track_online',
'website_event_track_exhibitor',
'website_event_track_session',
'website_event_track_live',
+7
View File
@@ -23,6 +23,10 @@ class EventDtPatcher(SavepointCase):
'odoo.addons.event.models.event_event.fields.Datetime',
wraps=FieldsDatetime
)
cls.wevent_dt = patch(
'odoo.addons.website_event.models.event_event.fields.Datetime',
wraps=FieldsDatetime
)
cls.wevent_main_dt = patch(
'odoo.addons.website_event.controllers.main.fields.Datetime',
wraps=FieldsDatetime
@@ -36,14 +40,17 @@ class EventDtPatcher(SavepointCase):
wraps=FieldsDate
)
cls.mock_event_dt = cls.event_dt.start()
cls.mock_wevent_dt = cls.wevent_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_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_dt.stop)
cls.addClassCleanup(cls.wevent_main_dt.stop)
cls.addClassCleanup(cls.event_date.stop)
cls.addClassCleanup(cls.wevent_main_date.stop)
@@ -14,9 +14,11 @@
'data/event_data.xml',
'data/event_track_data.xml',
'views/assets.xml',
'views/event_templates.xml',
'views/event_track_templates_agenda.xml',
'views/event_track_templates_list.xml',
'views/event_track_templates_misc.xml',
'views/event_track_templates_reminder.xml',
'views/event_track_templates_page.xml',
'views/event_track_templates_proposal.xml',
'views/website_templates.xml',
@@ -24,9 +26,11 @@
'views/event_track_location_views.xml',
'views/event_track_tag_views.xml',
'views/event_track_stage_views.xml',
'views/event_track_visitor_views.xml',
'views/event_sponsor_views.xml',
'views/event_event_views.xml',
'views/event_type_views.xml',
'views/website_visitor_views.xml',
'views/event_menus.xml',
],
'demo': [
@@ -36,5 +40,6 @@
'data/event_track_tag_demo.xml',
'data/event_track_demo.xml',
'data/event_track_demo_description.xml',
'data/event_track_visitor_demo.xml',
],
}
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main
from . import event_track
from . import webmanifest
@@ -1,85 +1,71 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from werkzeug.exceptions import Forbidden, NotFound
from datetime import timedelta
from werkzeug.exceptions import Forbidden, NotFound
import babel
import babel.dates
import base64
import pytz
from odoo import exceptions, http, fields
from odoo.addons.http_routing.models.ir_http import url_for
from odoo.addons.website_event_track.controllers.main import WebsiteEventTrackController
from odoo.http import request
from odoo.modules.module import get_module_resource
from odoo.tools import ustr
from odoo.tools.translate import _
from odoo.tools import plaintext2html, html2plaintext
class EventTrackOnlineController(WebsiteEventTrackController):
class EventTrackController(http.Controller):
def _event_agenda_get_tracks(self, event):
tracks_sudo = event.sudo().track_ids.filtered(lambda track: track.date)
if not request.env.user.has_group('event.group_event_manager'):
tracks_sudo = tracks_sudo.filtered(lambda track: track.is_published or track.stage_id.is_accepted)
return tracks_sudo
# ------------------------------------------------------------
# TRACK LIST VIEW
# ------------------------------------------------------------
def time_slot_rounder(self, time, rounded_minutes):
""" Rounds to nearest hour by adding a timedelta hour if minute >= rounded_minutes
E.g. : If rounded_minutes = 15 -> 09:26:00 becomes 09:30:00
09:17:00 becomes 09:15:00
"""
return (time.replace(second=0, microsecond=0, minute=0, hour=time.hour)
+ timedelta(minutes=rounded_minutes * (time.minute // rounded_minutes)))
@http.route([
'''/event/<model("event.event"):event>/track''',
'''/event/<model("event.event"):event>/track/tag/<model("event.track.tag"):tag>'''
], type='http', auth="public", website=True, sitemap=False)
def event_tracks(self, event, tag=None, **post):
if not event.can_access_from_current_website() or (tag and tag.color == 0):
raise NotFound()
def _split_track_by_days(self, track, local_tz):
"""
Based on the track start_date and the duration,
split the track duration into :
start_time by day : number of time slot (15 minutes) that the track takes on that day.
E.g. : start date = 01-01-2000 10:00 PM and duration = 3 hours
return {
01-01-2000 10:00:00 PM: 8 (2 * 4),
01-02-2000 00:00:00 AM: 4 (1 * 4)
}
Also return a set of all the time slots
"""
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
start_datetime = self.time_slot_rounder(start_date, 15)
end_datetime = self.time_slot_rounder(start_datetime + timedelta(hours=(track.duration or 0.25)), 15)
time_slots_count = int(((end_datetime - start_datetime).total_seconds() / 3600) * 4)
event = event.with_context(tz=event.date_tz or 'UTC')
searches = {}
if tag:
searches.update(tag=tag.id)
tracks = event.track_ids.filtered(lambda track: tag in track.tag_ids)
else:
tracks = event.track_ids
time_slots_by_day_start_time = {start_datetime: 0}
for i in range(0, time_slots_count):
# If the new time slot is still on the current day
next_day = (start_datetime + timedelta(days=1)).date()
if (start_datetime + timedelta(minutes=15*i)).date() <= next_day:
time_slots_by_day_start_time[start_datetime] += 1
else:
start_datetime = next_day.datetime()
time_slots_by_day_start_time[start_datetime] = 0
values = {
'event': event,
'main_object': event,
'tracks': tracks,
'tags': event.tracks_tag_ids,
'searches': searches,
'html2plaintext': html2plaintext
}
return request.render("website_event_track.tracks", values)
return time_slots_by_day_start_time
# ------------------------------------------------------------
# AGENDA VIEW
# ------------------------------------------------------------
def _get_occupied_cells(self, track, rowspan, locations, local_tz):
"""
In order to use only once the cells that the tracks will occupy, we need to reserve those cells
(time_slot, location) coordinate. Those coordinated will be given to the template to avoid adding
blank cells where already occupied by a track.
"""
occupied_cells = []
@http.route(['''/event/<model("event.event"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
def event_agenda(self, event, tag=None, **post):
if not event.can_access_from_current_website():
raise NotFound()
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
start_date = self.time_slot_rounder(start_date, 15)
for i in range(0, rowspan):
time_slot = start_date + timedelta(minutes=15*i)
if track.location_id:
occupied_cells.append((time_slot, track.location_id))
# when no location, reserve all locations
else:
occupied_cells += [(time_slot, location) for location in locations if location]
event = event.with_context(tz=event.date_tz or 'UTC')
vals = {
'event': event,
'main_object': event,
'tag': tag,
'user_event_manager': request.env.user.has_group('event.group_event_manager'),
}
return occupied_cells
vals.update(self._prepare_calendar_values(event))
return request.render("website_event_track.agenda_online", vals)
def _prepare_calendar_values(self, event):
"""
@@ -157,48 +143,90 @@ class EventTrackOnlineController(WebsiteEventTrackController):
'locations': locations
}
@http.route(['''/event/<model("event.event"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
def event_agenda(self, event, tag=None, **post):
def _event_agenda_get_tracks(self, event):
tracks_sudo = event.sudo().track_ids.filtered(lambda track: track.date)
if not request.env.user.has_group('event.group_event_manager'):
tracks_sudo = tracks_sudo.filtered(lambda track: track.is_published or track.stage_id.is_accepted)
return tracks_sudo
def _get_locale_time(self, dt_time, lang_code):
""" Get locale time from datetime object
:param dt_time: datetime object
:param lang_code: language code (eg. en_US)
"""
locale = babel.Locale.parse(lang_code)
return babel.dates.format_time(dt_time, format='short', locale=locale)
def time_slot_rounder(self, time, rounded_minutes):
""" Rounds to nearest hour by adding a timedelta hour if minute >= rounded_minutes
E.g. : If rounded_minutes = 15 -> 09:26:00 becomes 09:30:00
09:17:00 becomes 09:15:00
"""
return (time.replace(second=0, microsecond=0, minute=0, hour=time.hour)
+ timedelta(minutes=rounded_minutes * (time.minute // rounded_minutes)))
def _split_track_by_days(self, track, local_tz):
"""
Based on the track start_date and the duration,
split the track duration into :
start_time by day : number of time slot (15 minutes) that the track takes on that day.
E.g. : start date = 01-01-2000 10:00 PM and duration = 3 hours
return {
01-01-2000 10:00:00 PM: 8 (2 * 4),
01-02-2000 00:00:00 AM: 4 (1 * 4)
}
Also return a set of all the time slots
"""
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
start_datetime = self.time_slot_rounder(start_date, 15)
end_datetime = self.time_slot_rounder(start_datetime + timedelta(hours=(track.duration or 0.25)), 15)
time_slots_count = int(((end_datetime - start_datetime).total_seconds() / 3600) * 4)
time_slots_by_day_start_time = {start_datetime: 0}
for i in range(0, time_slots_count):
# If the new time slot is still on the current day
next_day = (start_datetime + timedelta(days=1)).date()
if (start_datetime + timedelta(minutes=15*i)).date() <= next_day:
time_slots_by_day_start_time[start_datetime] += 1
else:
start_datetime = next_day.datetime()
time_slots_by_day_start_time[start_datetime] = 0
return time_slots_by_day_start_time
def _get_occupied_cells(self, track, rowspan, locations, local_tz):
"""
In order to use only once the cells that the tracks will occupy, we need to reserve those cells
(time_slot, location) coordinate. Those coordinated will be given to the template to avoid adding
blank cells where already occupied by a track.
"""
occupied_cells = []
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
start_date = self.time_slot_rounder(start_date, 15)
for i in range(0, rowspan):
time_slot = start_date + timedelta(minutes=15*i)
if track.location_id:
occupied_cells.append((time_slot, track.location_id))
# when no location, reserve all locations
else:
occupied_cells += [(time_slot, location) for location in locations if location]
return occupied_cells
# ------------------------------------------------------------
# TRACK PAGE VIEW
# ------------------------------------------------------------
@http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event.id)]"):track>'''], type='http', auth="public", website=True, sitemap=True)
def event_track_view(self, event, track, **post):
if not event.can_access_from_current_website():
raise NotFound()
event = event.with_context(tz=event.date_tz or 'UTC')
vals = {
'event': event,
'main_object': event,
'tag': tag,
'user_event_manager': request.env.user.has_group('event.group_event_manager'),
}
vals.update(self._prepare_calendar_values(event))
return request.render("website_event_track_online.agenda_online", vals)
def _fetch_track(self, track_id, allow_is_accepted=False):
track = request.env['event.track'].browse(track_id).exists()
if not track:
raise NotFound()
try:
track.check_access_rights('read')
track.check_access_rule('read')
except exceptions.AccessError:
track_sudo = track.sudo()
if allow_is_accepted and track_sudo.is_accepted:
track = track_sudo
else:
raise Forbidden()
event = track.event_id
# JSON RPC have no website in requests
if hasattr(request, 'website_id') and not event.can_access_from_current_website():
raise NotFound()
try:
event.check_access_rights('read')
event.check_access_rule('read')
except exceptions.AccessError:
raise Forbidden()
return track
track = track.sudo().with_context(tz=event.date_tz or 'UTC')
values = {'track': track, 'event': track.event_id, 'main_object': track}
return request.render("website_event_track.track_view", values)
@http.route("/event/track/toggle_reminder", type="json", auth="public", website=True)
def track_reminder_toggle(self, track_id, set_reminder_on):
@@ -232,45 +260,73 @@ class EventTrackOnlineController(WebsiteEventTrackController):
return result
@http.route('/event/manifest.webmanifest', type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def webmanifest(self):
""" Returns a WebManifest describing the metadata associated with a web application.
Using this metadata, user agents can provide developers with means to create user
experiences that are more comparable to that of a native application.
"""
company = request.env.company
website = request.website
manifest = {
'name': _('%s Online Events') % company.name,
'short_name': company.name,
'description': _('%s Online Events') % company.name,
'scope': url_for('/event'),
'start_url': url_for('/event'),
'display': 'standalone',
'background_color': '#ffffff',
'theme_color': '#875A7B',
}
icon_sizes = ['192x192', '512x512']
manifest['icons'] = [{
'src': website.image_url(website, 'app_icon', size=size),
'sizes': size,
'type': 'image/png',
} for size in icon_sizes]
body = json.dumps(manifest, default=ustr)
response = request.make_response(body, [
('Content-Type', 'application/manifest+json'),
])
return response
# ------------------------------------------------------------
# TRACK PROPOSAL
# ------------------------------------------------------------
@http.route('/event/service-worker.js', type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def service_worker(self):
""" Returns a ServiceWorker javascript file scoped for website_event
"""
sw_file = get_module_resource('website_event_track_online', 'static/src/js/service_worker.js')
with open(sw_file, 'rb') as fp:
body = fp.read()
response = request.make_response(body, [
('Content-Type', 'text/javascript'),
('Service-Worker-Allowed', url_for('/event')),
])
return response
@http.route(['''/event/<model("event.event"):event>/track_proposal'''], type='http', auth="public", website=True, sitemap=False)
def event_track_proposal(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
return request.render("website_event_track.event_track_proposal", {'event': event, 'main_object': event})
@http.route(['''/event/<model("event.event"):event>/track_proposal/post'''], type='http', auth="public", methods=['POST'], website=True)
def event_track_proposal_post(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
tags = []
for tag in event.allowed_track_tag_ids:
if post.get('tag_' + str(tag.id)):
tags.append(tag.id)
track = request.env['event.track'].sudo().create({
'name': post['track_name'],
'partner_name': post['partner_name'],
'partner_email': post['email_from'],
'partner_phone': post['phone'],
'partner_biography': plaintext2html(post['biography']),
'event_id': event.id,
'tag_ids': [(6, 0, tags)],
'user_id': False,
'description': plaintext2html(post['description']),
'image': base64.b64encode(post['image'].read()) if post.get('image') else False
})
if request.env.user != request.website.user_id:
track.sudo().message_subscribe(partner_ids=request.env.user.partner_id.ids)
else:
partner = request.env['res.partner'].sudo().search([('email', '=', post['email_from'])])
if partner:
track.sudo().message_subscribe(partner_ids=partner.ids)
return request.render("website_event_track.event_track_proposal", {'track': track, 'event': event})
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def _fetch_track(self, track_id, allow_is_accepted=False):
track = request.env['event.track'].browse(track_id).exists()
if not track:
raise NotFound()
try:
track.check_access_rights('read')
track.check_access_rule('read')
except exceptions.AccessError:
track_sudo = track.sudo()
if allow_is_accepted and track_sudo.is_accepted:
track = track_sudo
else:
raise Forbidden()
event = track.event_id
# JSON RPC have no website in requests
if hasattr(request, 'website_id') and not event.can_access_from_current_website():
raise NotFound()
try:
event.check_access_rights('read')
event.check_access_rule('read')
except exceptions.AccessError:
raise Forbidden()
return track
@@ -1,185 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import babel
import babel.dates
import collections
import datetime
import pytz
import base64
from werkzeug.exceptions import NotFound
from odoo import fields, http
from odoo.http import request
from odoo.tools import plaintext2html, html2plaintext
class WebsiteEventTrackController(http.Controller):
# ------------------------------------------------------------
# TRACK LIST VIEW
# ------------------------------------------------------------
@http.route([
'''/event/<model("event.event"):event>/track''',
'''/event/<model("event.event"):event>/track/tag/<model("event.track.tag"):tag>'''
], type='http', auth="public", website=True, sitemap=False)
def event_tracks(self, event, tag=None, **post):
if not event.can_access_from_current_website() or (tag and tag.color == 0):
raise NotFound()
event = event.with_context(tz=event.date_tz or 'UTC')
searches = {}
if tag:
searches.update(tag=tag.id)
tracks = event.track_ids.filtered(lambda track: tag in track.tag_ids)
else:
tracks = event.track_ids
values = {
'event': event,
'main_object': event,
'tracks': tracks,
'tags': event.tracks_tag_ids,
'searches': searches,
'html2plaintext': html2plaintext
}
return request.render("website_event_track.tracks", values)
# ------------------------------------------------------------
# AGENDA VIEW
# ------------------------------------------------------------
def _get_locale_time(self, dt_time, lang_code):
""" Get locale time from datetime object
:param dt_time: datetime object
:param lang_code: language code (eg. en_US)
"""
locale = babel.Locale.parse(lang_code)
return babel.dates.format_time(dt_time, format='short', locale=locale)
def _prepare_calendar(self, event, event_track_ids):
local_tz = pytz.timezone(event.date_tz or 'UTC')
lang_code = request.env.context.get('lang')
locations = {} # { location: [track, start_date, end_date, rowspan]}
dates = [] # [ (date, {}) ]
for track in event_track_ids:
locations.setdefault(track.location_id or False, [])
forcetr = True
for track in event_track_ids:
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
end_date = start_date + datetime.timedelta(hours=(track.duration or 0.5))
location = track.location_id or False
locations.setdefault(location, [])
# New TR, align all events
if forcetr or (start_date>dates[-1][0]) or not location:
formatted_time = self._get_locale_time(start_date, lang_code)
dates.append((start_date, {}, bool(location), formatted_time))
for loc in list(locations):
if locations[loc] and (locations[loc][-1][2] > start_date):
locations[loc][-1][3] += 1
elif not locations[loc] or locations[loc][-1][2] <= start_date:
locations[loc].append([False, locations[loc] and locations[loc][-1][2] or dates[0][0], start_date, 1])
dates[-1][1][loc] = locations[loc][-1]
forcetr = not bool(location)
# Add event
if locations[location] and locations[location][-1][1] > start_date:
locations[location][-1][3] -= 1
locations[location].append([track, start_date, end_date, 1])
dates[-1][1][location] = locations[location][-1]
locations = collections.OrderedDict(sorted(locations.items(), key=lambda t: t[0].id if t[0] else 0))
return {
'locations': locations,
'dates': dates
}
@http.route(['''/event/<model("event.event"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
def event_agenda(self, event, tag=None, **post):
if not event.can_access_from_current_website():
raise NotFound()
event = event.with_context(tz=event.date_tz or 'UTC')
local_tz = pytz.timezone(event.date_tz or 'UTC')
days_tracks = collections.defaultdict(lambda: [])
event_track_ids = self._event_agenda_get_tracks(event)
for track in event_track_ids.sorted(lambda track: (bool(track.date), track.date, bool(track.location_id))):
if not track.date:
continue
date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
days_tracks[str(date)[:10]].append(track)
days = {}
tracks_by_days = {}
for day, tracks in days_tracks.items():
tracks_by_days[day] = tracks
days[day] = self._prepare_calendar(event, tracks)
return request.render("website_event_track.agenda", {
'event': event,
'main_object': event,
'days': days,
'tracks_by_days': tracks_by_days,
'tag': tag,
'user_event_manager': request.env.user.has_group('event.group_event_manager'),
})
def _event_agenda_get_tracks(self, event):
return event.track_ids
# ------------------------------------------------------------
# TRACK PAGE VIEW
# ------------------------------------------------------------
@http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event.id)]"):track>'''], type='http', auth="public", website=True, sitemap=True)
def event_track_view(self, event, track, **post):
if not event.can_access_from_current_website():
raise NotFound()
track = track.sudo().with_context(tz=event.date_tz or 'UTC')
values = {'track': track, 'event': track.event_id, 'main_object': track}
return request.render("website_event_track.track_view", values)
# ------------------------------------------------------------
# TRACK PROPOSAL
# ------------------------------------------------------------
@http.route(['''/event/<model("event.event"):event>/track_proposal'''], type='http', auth="public", website=True, sitemap=False)
def event_track_proposal(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
return request.render("website_event_track.event_track_proposal", {'event': event, 'main_object': event})
@http.route(['''/event/<model("event.event"):event>/track_proposal/post'''], type='http', auth="public", methods=['POST'], website=True)
def event_track_proposal_post(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
tags = []
for tag in event.allowed_track_tag_ids:
if post.get('tag_' + str(tag.id)):
tags.append(tag.id)
track = request.env['event.track'].sudo().create({
'name': post['track_name'],
'partner_name': post['partner_name'],
'partner_email': post['email_from'],
'partner_phone': post['phone'],
'partner_biography': plaintext2html(post['biography']),
'event_id': event.id,
'tag_ids': [(6, 0, tags)],
'user_id': False,
'description': plaintext2html(post['description']),
'image': base64.b64encode(post['image'].read()) if post.get('image') else False
})
if request.env.user != request.website.user_id:
track.sudo().message_subscribe(partner_ids=request.env.user.partner_id.ids)
else:
partner = request.env['res.partner'].sudo().search([('email', '=', post['email_from'])])
if partner:
track.sudo().message_subscribe(partner_ids=partner.ids)
return request.render("website_event_track.event_track_proposal", {'track': track, 'event': event})
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
import pytz
from odoo import http
from odoo.addons.http_routing.models.ir_http import url_for
from odoo.http import request
from odoo.modules.module import get_module_resource
from odoo.tools import ustr
from odoo.tools.translate import _
class TrackManifest(http.Controller):
@http.route('/event/manifest.webmanifest', type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def webmanifest(self):
""" Returns a WebManifest describing the metadata associated with a web application.
Using this metadata, user agents can provide developers with means to create user
experiences that are more comparable to that of a native application.
"""
company = request.env.company
website = request.website
manifest = {
'name': _('%s Online Events') % company.name,
'short_name': company.name,
'description': _('%s Online Events') % company.name,
'scope': url_for('/event'),
'start_url': url_for('/event'),
'display': 'standalone',
'background_color': '#ffffff',
'theme_color': '#875A7B',
}
icon_sizes = ['192x192', '512x512']
manifest['icons'] = [{
'src': website.image_url(website, 'app_icon', size=size),
'sizes': size,
'type': 'image/png',
} for size in icon_sizes]
body = json.dumps(manifest, default=ustr)
response = request.make_response(body, [
('Content-Type', 'application/manifest+json'),
])
return response
@http.route('/event/service-worker.js', type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def service_worker(self):
""" Returns a ServiceWorker javascript file scoped for website_event
"""
sw_file = get_module_resource('website_event_track', 'static/src/js/service_worker.js')
with open(sw_file, 'rb') as fp:
body = fp.read()
response = request.make_response(body, [
('Content-Type', 'text/javascript'),
('Service-Worker-Allowed', url_for('/event')),
])
return response
@@ -33,14 +33,17 @@
<record id="event_sponsor_type1" model="event.sponsor.type">
<field name="name">Bronze</field>
<field name="sequence">3</field>
<field name="display_ribbon_style">Bronze</field>
</record>
<record id="event_sponsor_type2" model="event.sponsor.type">
<field name="name">Silver</field>
<field name="sequence">2</field>
<field name="display_ribbon_style">Silver</field>
</record>
<record id="event_sponsor_type3" model="event.sponsor.type">
<field name="name">Gold</field>
<field name="sequence">1</field>
<field name="display_ribbon_style">Gold</field>
</record>
<record id="event_track_stage0" model="event.track.stage">
@@ -58,11 +61,13 @@
<field name="name">Announced</field>
<field name="sequence">3</field>
<field name="color">3</field>
<field name="is_accepted" eval="True"/>
</record>
<record id="event_track_stage3" model="event.track.stage">
<field name="name">Published</field>
<field name="sequence">4</field>
<field name="color">4</field>
<field name="is_accepted" eval="True"/>
<field name="is_done" eval="True"/>
</record>
<record id="event_track_stage4" model="event.track.stage">
@@ -333,6 +333,7 @@
<field name="color">1</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="wishlisted_by_default" eval="True"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 06:00:00')"></field>
<field name="tag_ids" eval="[
(4, ref('website_event_track.event_track_tag1')),
@@ -474,6 +475,7 @@
<field name="color">1</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="wishlisted_by_default" eval="True"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 06:00:00')"></field>
<field name="tag_ids" eval="[
(4, ref('website_event_track.event_track_tag1')),
@@ -519,6 +521,7 @@
<field name="color">2</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="wishlisted_by_default" eval="True"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 12:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3'))]"/>
@@ -572,6 +575,7 @@
<field name="color">7</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="wishlisted_by_default" eval="True"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 13:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
@@ -1,28 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event_track_tag_category_1" model="event.track.tag.category">
<field name="name">Audience</field>
<field name="sequence">10</field>
</record>
<record id="event_track_tag_category_2" model="event.track.tag.category">
<field name="name">Format</field>
<field name="sequence">20</field>
</record>
<record id="event_track_tag1" model="event.track.tag">
<field name="name">Consumers</field>
<field name="color">1</field>
<field name="category_id" ref="event_track_tag_category_1"/>
<field name="sequence">1</field>
</record>
<record id="event_track_tag2" model="event.track.tag">
<field name="name">Sales</field>
<field name="color">2</field>
<field name="category_id" ref="event_track_tag_category_1"/>
<field name="sequence">2</field>
</record>
<record id="event_track_tag3" model="event.track.tag">
<field name="name">Research</field>
<field name="color">3</field>
<field name="category_id" ref="event_track_tag_category_1"/>
<field name="sequence">3</field>
</record>
<record id="event_track_tag11" model="event.track.tag">
<field name="name">Lightning Talks</field>
<field name="color">4</field>
<field name="category_id" ref="event_track_tag_category_2"/>
<field name="sequence">11</field>
</record>
<record id="event_track_tag12" model="event.track.tag">
<field name="name">Round Table</field>
<field name="color">5</field>
<field name="category_id" ref="event_track_tag_category_2"/>
<field name="sequence">12</field>
</record>
<record id="event_track_tag13" model="event.track.tag">
<field name="name">Keynote</field>
<field name="color">6</field>
<field name="category_id" ref="event_track_tag_category_2"/>
<field name="sequence">13</field>
</record>
</odoo>
@@ -8,6 +8,10 @@ from . import event_track
from . import event_track_location
from . import event_track_stage
from . import event_track_tag
from . import event_track_tag_category
from . import event_track_visitor
from . import event_type
from . import website
from . import website_event_menu
from . import website_menu
from . import website_visitor
@@ -2,27 +2,40 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.modules.module import get_resource_path
class Sponsor(models.Model):
_name = "event.sponsor"
_description = 'Event Sponsor'
_order = "sequence, sponsor_type_id"
_rec_name = 'partner_name'
_rec_name = 'name'
_inherit = ['mail.thread', 'mail.activity.mixin']
event_id = fields.Many2one('event.event', 'Event', required=True)
sponsor_type_id = fields.Many2one('event.sponsor.type', 'Sponsoring Type', required=True)
url = fields.Char('Sponsor Website', compute='_compute_url', readonly=False, store=True)
sequence = fields.Integer('Sequence')
active = fields.Boolean(default=True)
# contact information
partner_id = fields.Many2one('res.partner', 'Sponsor/Customer', required=True)
partner_name = fields.Char('Name', related='partner_id.name')
partner_email = fields.Char('Email', related='partner_id.email')
partner_phone = fields.Char('Phone', related='partner_id.phone')
partner_mobile = fields.Char('Mobile', related='partner_id.mobile')
url = fields.Char('Sponsor Website', compute='_compute_url', readonly=False, store=True)
sequence = fields.Integer('Sequence')
image_128 = fields.Image(
string="Logo", related='partner_id.image_128', store=True, readonly=False)
active = fields.Boolean(default=True)
name = fields.Char('Sponsor Name', compute='_compute_name', readonly=False, store=True)
email = fields.Char('Sponsor Email', compute='_compute_email', readonly=False, store=True)
phone = fields.Char('Sponsor Phone', compute='_compute_phone', readonly=False, store=True)
mobile = fields.Char('Sponsor Mobile', compute='_compute_mobile', readonly=False, store=True)
# image
image_512 = fields.Image(
string="Logo", max_width=512, max_height=512,
compute='_compute_image_512', readonly=False, store=True)
image_256 = fields.Image("Image 256", related="image_512", max_width=256, max_height=256, store=False)
image_128 = fields.Image("Image 128", related="image_512", max_width=128, max_height=128, store=False)
website_image_url = fields.Char(
string='Image URL', max_width=256, max_height=256,
compute='_compute_website_image_url', compute_sudo=True, store=False)
@api.depends('partner_id')
def _compute_url(self):
@@ -30,6 +43,47 @@ class Sponsor(models.Model):
if sponsor.partner_id.website or not sponsor.url:
sponsor.url = sponsor.partner_id.website
@api.depends('partner_id')
def _compute_name(self):
self._synchronize_with_partner('name')
@api.depends('partner_id')
def _compute_email(self):
self._synchronize_with_partner('email')
@api.depends('partner_id')
def _compute_phone(self):
self._synchronize_with_partner('phone')
@api.depends('partner_id')
def _compute_mobile(self):
self._synchronize_with_partner('mobile')
@api.depends('partner_id')
def _compute_image_512(self):
self._synchronize_with_partner('image_512')
@api.depends('image_256', 'partner_id.image_256')
def _compute_website_image_url(self):
for sponsor in self:
if sponsor.image_256:
sponsor.website_image_url = self.env['website'].image_url(sponsor, 'image_256', size=256)
elif sponsor.partner_id.image_256:
sponsor.website_image_url = self.env['website'].image_url(sponsor.partner_id, 'image_256', size=256)
else:
sponsor.website_image_url = get_resource_path('website_event_track', 'static/src/img', 'event_sponsor_default_%d.png' % (sponsor.id % 1))
def _synchronize_with_partner(self, fname):
""" Synchronize with partner if not set. Setting a value does not write
on partner as this may be event-specific information. """
for sponsor in self:
if not sponsor[fname]:
sponsor[fname] = sponsor.partner_id[fname]
# ------------------------------------------------------------
# MESSAGING
# ------------------------------------------------------------
def _message_get_suggested_recipients(self):
recipients = super(Sponsor, self)._message_get_suggested_recipients()
for sponsor in self:
@@ -11,3 +11,8 @@ class SponsorType(models.Model):
name = fields.Char('Sponsor Type', required=True, translate=True)
sequence = fields.Integer('Sequence')
display_ribbon_style = fields.Selection([
('no_ribbon', 'No Ribbon'),
('Gold', 'Gold'),
('Silver', 'Silver'),
('Bronze', 'Bronze')], string='Ribbon Style')
@@ -4,8 +4,10 @@
from datetime import timedelta
from odoo import api, fields, models
from odoo.tools.translate import _, html_translate
from odoo.addons.http_routing.models.ir_http import slug
from odoo.osv import expression
from odoo.tools.mail import is_html_empty
from odoo.tools.translate import _, html_translate
class Track(models.Model):
@@ -21,7 +23,6 @@ class Track(models.Model):
# description
name = fields.Char('Title', required=True, translate=True)
event_id = fields.Many2one('event.event', 'Event', required=True)
image = fields.Image("Image", max_width=128, max_height=128)
active = fields.Boolean(default=True)
user_id = fields.Many2one('res.users', 'Responsible', tracking=True, default=lambda self: self.env.user)
company_id = fields.Many2one('res.company', related='event_id.company_id')
@@ -38,6 +39,7 @@ class Track(models.Model):
index=True, copy=False, default=_get_default_stage_id,
group_expand='_read_group_stage_ids',
required=True, tracking=True)
is_accepted = fields.Boolean('Is Accepted', related='stage_id.is_accepted', readonly=True)
kanban_state = fields.Selection([
('normal', 'Grey'),
('done', 'Green'),
@@ -58,12 +60,46 @@ class Track(models.Model):
partner_phone = fields.Char(
string='Phone', compute='_compute_partner_info',
readonly=False, store=True, tracking=30)
partner_biography = fields.Html(string='Biography')
partner_biography = fields.Html(
string='Biography', compute='_compute_partner_biography',
readonly=False, store=True)
partner_function = fields.Char(
'Job Position', related='partner_id.function',
compute_sudo=True, readonly=True)
partner_company_name = fields.Char(
'Company Name', related='partner_id.parent_name',
compute_sudo=True, readonly=True)
image = fields.Image(
string="Speaker Photo", compute="_compute_speaker_image",
readonly=False, store=True,
max_width=256, max_height=256)
location_id = fields.Many2one('event.track.location', 'Location')
# time information
date = fields.Datetime('Track Date')
date_end = fields.Datetime('Track End Date', compute='_compute_end_date', store=True)
duration = fields.Float('Duration', default=1.5, help="Track duration in hours.")
# frontend description
website_image = fields.Image(string="Website Image", max_width=1024, max_height=1024)
website_image_url = fields.Char(
string='Image URL', compute='_compute_website_image_url',
compute_sudo=True, store=False)
# wishlist / visitors management
event_track_visitor_ids = fields.One2many(
'event.track.visitor', 'track_id', string="Track Visitors",
groups="event.group_event_user")
is_reminder_on = fields.Boolean('Is Reminder On', compute='_compute_is_reminder_on')
wishlist_visitor_ids = fields.Many2many(
'website.visitor', string="Visitor Wishlist",
compute="_compute_wishlist_visitor_ids", compute_sudo=True,
search="_search_wishlist_visitor_ids",
groups="event.group_event_user")
wishlist_visitor_count = fields.Integer(
string="# Wishlisted",
compute="_compute_wishlist_visitor_ids", compute_sudo=True,
groups="event.group_event_user")
wishlisted_by_default = fields.Boolean(
string='Always Wishlisted',
help="""If set, the talk will be starred for each attendee registered to the event. The attendee won't be able to un-star this talk.""")
@api.depends('name')
def _compute_website_url(self):
@@ -72,6 +108,8 @@ class Track(models.Model):
if track.id:
track.website_url = '/event/%s/track/%s' % (slug(track.event_id), slug(track))
# SPEAKER
@api.depends('partner_id')
def _compute_partner_info(self):
for track in self:
@@ -80,6 +118,23 @@ class Track(models.Model):
track.partner_email = track.partner_id.email
track.partner_phone = track.partner_id.phone
@api.depends('partner_id')
def _compute_partner_biography(self):
for track in self:
if not track.partner_biography:
track.partner_biography = track.partner_id.website_description
elif track.partner_id and is_html_empty(track.partner_biography) and \
not is_html_empty(track.partner_id.website_description):
track.partner_biography = track.partner_id.website_description
@api.depends('partner_id')
def _compute_speaker_image(self):
for track in self:
if not track.image:
track.image = track.partner_id.image_256
# TIME
@api.depends('date', 'duration')
def _compute_end_date(self):
for track in self:
@@ -89,6 +144,85 @@ class Track(models.Model):
else:
track.date_end = False
# FRONTEND DESCRIPTION
@api.depends('image', 'partner_id.image_256')
def _compute_website_image_url(self):
for track in self:
if track.website_image:
track.website_image_url = self.env['website'].image_url(track, 'website_image', size=1024)
else:
track.website_image_url = '/website_event_track/static/src/img/event_track_default_%d.jpeg' % (track.id % 2)
# WISHLIST / VISITOR MANAGEMENT
@api.depends('wishlisted_by_default', 'event_track_visitor_ids.visitor_id',
'event_track_visitor_ids.partner_id', 'event_track_visitor_ids.is_wishlisted',
'event_track_visitor_ids.is_blacklisted')
@api.depends_context('uid')
def _compute_is_reminder_on(self):
current_visitor = self.env['website.visitor']._get_visitor_from_request(force_create=False)
if self.env.user._is_public() and not current_visitor:
for track in self:
track.is_reminder_on = track.wishlisted_by_default
else:
if self.env.user._is_public():
domain = [('visitor_id', '=', current_visitor.id)]
elif 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)]
event_track_visitors = self.env['event.track.visitor'].sudo().search_read(
expression.AND([
domain,
[('track_id', 'in', self.ids)]
]), fields=['track_id', 'is_wishlisted', 'is_blacklisted']
)
wishlist_map = {
track_visitor['track_id'][0]: {
'is_wishlisted': track_visitor['is_wishlisted'],
'is_blacklisted': track_visitor['is_blacklisted']
} for track_visitor in event_track_visitors
}
for track in self:
if wishlist_map.get(track.id):
track.is_reminder_on = wishlist_map.get(track.id)['is_wishlisted'] or (track.wishlisted_by_default and not wishlist_map[track.id]['is_blacklisted'])
else:
track.is_reminder_on = track.wishlisted_by_default
@api.depends('event_track_visitor_ids.visitor_id', 'event_track_visitor_ids.is_wishlisted')
def _compute_wishlist_visitor_ids(self):
results = self.env['event.track.visitor'].read_group(
[('track_id', 'in', self.ids), ('is_wishlisted', '=', True)],
['track_id', 'visitor_id:array_agg'],
['track_id']
)
visitor_ids_map = {result['track_id'][0]: result['visitor_id'] for result in results}
for track in self:
track.wishlist_visitor_ids = visitor_ids_map.get(track.id, [])
track.wishlist_visitor_count = len(visitor_ids_map.get(track.id, []))
def _search_wishlist_visitor_ids(self, operator, operand):
if operator == "not in":
raise NotImplementedError("Unsupported 'Not In' operation on track wishlist visitors")
track_visitors = self.env['event.track.visitor'].sudo().search([
('visitor_id', operator, operand),
('is_wishlisted', '=', True)
])
return [('id', 'in', track_visitors.track_id.ids)]
# ------------------------------------------------------------
# CRUD
# ------------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
tracks = super(Track, self).create(vals_list)
@@ -100,12 +234,16 @@ class Track(models.Model):
subject=track.name,
subtype_id=self.env.ref('website_event_track.mt_event_track').id,
)
track._synchronize_with_stage(track.stage_id)
return tracks
def write(self, vals):
if 'stage_id' in vals and 'kanban_state' not in vals:
vals['kanban_state'] = 'normal'
if vals.get('stage_id'):
stage = self.env['event.track.stage'].browse(vals['stage_id'])
self._synchronize_with_stage(stage)
res = super(Track, self).write(vals)
if vals.get('partner_id'):
self.message_subscribe([vals['partner_id']])
@@ -116,6 +254,14 @@ class Track(models.Model):
""" Always display all stages """
return stages.search([], order=order)
def _synchronize_with_stage(self, stage):
if stage.is_done:
self.is_published = True
# ------------------------------------------------------------
# MESSAGING
# ------------------------------------------------------------
def _track_template(self, changes):
res = super(Track, self)._track_template(changes)
track = self[0]
@@ -157,6 +303,10 @@ class Track(models.Model):
]).write({'partner_id': new_partner.id})
return super(Track, self)._message_post_after_hook(message, msg_vals)
# ------------------------------------------------------------
# ACTION
# ------------------------------------------------------------
def open_track_speakers_list(self):
return {
'name': _('Speakers'),
@@ -166,3 +316,39 @@ class Track(models.Model):
'view_id': False,
'type': 'ir.actions.act_window',
}
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def _get_event_track_visitors(self, force_create=False):
self.ensure_one()
force_visitor_create = self.env.user._is_public()
visitor_sudo = self.env['website.visitor']._get_visitor_from_request(force_create=force_visitor_create)
if visitor_sudo:
visitor_sudo._update_visitor_last_visit()
if self.env.user._is_public():
domain = [('visitor_id', '=', visitor_sudo.id)]
elif visitor_sudo:
domain = [
'|',
('partner_id', '=', self.env.user.partner_id.id),
('visitor_id', '=', visitor_sudo.id)
]
else:
domain = [('partner_id', '=', self.env.user.partner_id.id)]
track_visitors = self.env['event.track.visitor'].sudo().search(
expression.AND([domain, [('track_id', 'in', self.ids)]])
)
missing = self - track_visitors.track_id
if missing and force_create:
track_visitors += self.env['event.track.visitor'].sudo().create([{
'visitor_id': visitor_sudo.id,
'partner_id': self.env.user.partner_id.id if not self.env.user._is_public() else False,
'track_id': track.id,
} for track in missing])
return track_visitors
@@ -18,6 +18,12 @@ class TrackStage(models.Model):
fold = fields.Boolean(
string='Folded in Kanban',
help='This stage is folded in the kanban view when there are no records in that stage to display.')
is_done = fields.Boolean(string='Done Stage')
is_accepted = fields.Boolean(
string='Accepted Stage',
help='Accepted tracks are displayed in agenda views but not accessible.')
is_done = fields.Boolean(
string='Done Stage',
help='Done tracks are automatically published so that they are available in frontend.')
is_cancel = fields.Boolean(string='Canceled Stage')
is_done = fields.Boolean()
color = fields.Integer(string='Color')
@@ -9,7 +9,7 @@ from odoo import fields, models
class TrackTag(models.Model):
_name = "event.track.tag"
_description = 'Event Track Tag'
_order = 'name'
_order = "category_id, sequence, name"
def _default_color(self):
return randint(1, 11)
@@ -19,6 +19,8 @@ class TrackTag(models.Model):
color = fields.Integer(
string='Color Index', default=lambda self: self._default_color(),
help="Note that colorless tags won't be available on the website.")
sequence = fields.Integer('Sequence', default=10)
category_id = fields.Many2one('event.track.tag.category', string="Category", ondelete="set null")
_sql_constraints = [
('name_uniq', 'unique (name)', "Tag name already exists !"),
@@ -12,11 +12,3 @@ class TrackTagCategory(models.Model):
name = fields.Char("Name", required=True, translate=True)
sequence = fields.Integer('Sequence', default=10)
tag_ids = fields.One2many('event.track.tag', 'category_id', string="Tags")
class TrackTag(models.Model):
_inherit = "event.track.tag"
_order = "category_id, sequence, name"
sequence = fields.Integer('Sequence', default=10)
category_id = fields.Many2one('event.track.tag.category', string="Category", ondelete="set null")
@@ -12,3 +12,7 @@ access_event_track_sponsor_manager,event.track.sponsor.type.public,model_event_s
access_event_track_sponsor_public,event.track.sponsor.public,model_event_sponsor,,1,0,0,0
access_event_track_stage_all,event.track.stage.all,model_event_track_stage,,1,0,0,0
access_event_track_stage_event_manager,event.track.stage.event.manager,model_event_track_stage,event.group_event_manager,1,1,1,1
event_track_visitor_access_all,event.track.visitor.access.all,model_event_track_visitor,,0,0,0,0
event_track_visitor_access_manager,event.track.visitor.access.manager,model_event_track_visitor,event.group_event_manager,1,1,1,1
event_track_tag_category_access_public,event.track.tag.category.access,model_event_track_tag_category,,1,0,0,0
event_track_tag_category_access_manager,event.track.tag.category.access.manager,model_event_track_tag_category,event.group_event_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
12 access_event_track_sponsor_public event.track.sponsor.public model_event_sponsor 1 0 0 0
13 access_event_track_stage_all event.track.stage.all model_event_track_stage 1 0 0 0
14 access_event_track_stage_event_manager event.track.stage.event.manager model_event_track_stage event.group_event_manager 1 1 1 1
15 event_track_visitor_access_all event.track.visitor.access.all model_event_track_visitor 0 0 0 0
16 event_track_visitor_access_manager event.track.visitor.access.manager model_event_track_visitor event.group_event_manager 1 1 1 1
17 event_track_tag_category_access_public event.track.tag.category.access model_event_track_tag_category 1 0 0 0
18 event_track_tag_category_access_manager event.track.tag.category.access.manager model_event_track_tag_category event.group_event_manager 1 1 1 1
@@ -1,4 +1,4 @@
importScripts("/website_event_track_online/static/lib/idb-keyval/idb-keyval.js");
importScripts("/website_event_track/static/lib/idb-keyval/idb-keyval.js");
const PREFIX = "odoo-event";
const SYNCABLE_ROUTES = ["/event/track/toggle_reminder"];
@@ -1,4 +1,4 @@
odoo.define("website_event_track_online.website_event_pwa_widget", function (require) {
odoo.define("website_event_track.website_event_pwa_widget", function (require) {
"use strict";
var config = require("web.config");
@@ -6,7 +6,7 @@ odoo.define("website_event_track_online.website_event_pwa_widget", function (req
var utils = require("web.utils");
var PWAInstallBanner = publicWidget.Widget.extend({
xmlDependencies: ["/website_event_track_online/static/src/xml/website_event_pwa.xml"],
xmlDependencies: ["/website_event_track/static/src/xml/website_event_pwa.xml"],
template: "pwa_install_banner",
events: {
"click .o_btn_install": "_onClickInstall",
@@ -1,94 +0,0 @@
odoo.define('website_event_track.set_customize_options', function (require) {
"use strict";
var EventSpecificOptions = require('website_event.set_customize_options').EventSpecificOptions;
EventSpecificOptions.include({
xmlDependencies: (EventSpecificOptions.prototype.xmlDependencies || [])
.concat([
'/website_event_track/static/src/xml/customize_options.xml',
]),
events: _.extend({}, EventSpecificOptions.prototype.events, {
'change #display-showcase-talks': '_onDisplayTalksChange',
'change #allow-talk-proposal': '_onAllowTalkProposalChange',
}),
start: function () {
this.$displayTalksInput = this.$('#display-showcase-talks');
this.$allowTalksInput = this.$('#allow-talk-proposal');
this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
_onAllowTalkProposalChange: function () {
var checkboxValue = this.$allowTalksInput.is(':checked');
this._toggleTalkProposal(checkboxValue);
},
_onDisplayTalksChange: function () {
var checkboxValue = this.$displayTalksInput.is(':checked');
this._toggleDisplayTalks(checkboxValue);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
_getCheckboxFields: function () {
var fields = this._super();
fields = _.union(fields, ['website_track', 'website_track_proposal']);
return fields;
},
_getCheckboxFieldMatch: function (checkboxField) {
if (checkboxField === 'website_track') {
return this.$displayTalksInput;
}
if (checkboxField === 'website_track_proposal') {
return this.$allowTalksInput;
}
return this._super(checkboxField);
},
_initCheckboxCallback: function (rpcData) {
this._super(rpcData);
var submenuInput;
if (rpcData[0]['website_track']) {
submenuInput = this._getCheckboxFieldMatch('website_track');
submenuInput.attr('checked', 'checked');
}
if (rpcData[0]['website_track_proposal']) {
submenuInput = this._getCheckboxFieldMatch('website_track_proposal');
submenuInput.attr('checked', 'checked');
}
},
_toggleTalkProposal: function (val) {
var self = this;
this._rpc({
model: this.modelName,
method: 'toggle_website_track_proposal',
args: [[this.eventId], val],
}).then(function () {
self._reloadEventPage();
});
},
_toggleDisplayTalks: function (val) {
var self = this;
this._rpc({
model: this.modelName,
method: 'toggle_website_track',
args: [[this.eventId], val],
}).then(function () {
self._reloadEventPage();
});
}
});
});
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="website_event.customize_options" t-extend="website_event.customize_options">
<t t-jquery="a[name='display-website-menu']" t-operation="after">
<a href="#" name="display-showcase-talks" class="dropdown-item" role="menuitem">
<label class="o_switch" for="display-showcase-talks">
<input id="display-showcase-talks" type="checkbox"/>
<span/>
Showcase Talks
</label>
</a>
<a href="#" name="allow-talk-proposal" class="dropdown-item" role="menuitem">
<label class="o_switch" for="allow-talk-proposal">
<input id="allow-talk-proposal" type="checkbox"/>
<span/>
Allow Talks Proposals
</label>
</a>
</t>
</t>
</templates>
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
from . import test_website_event
+8 -2
View File
@@ -2,10 +2,16 @@
<odoo>
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Website Event Track Assets">
<xpath expr="." position="inside">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" href="/website_event_track/static/src/css/website_event_track.css"/>
<link rel="stylesheet" type="text/scss" href="/website_event_track/static/src/scss/event_track_templates.scss"/>
<link rel="stylesheet" type="text/scss" href="/website_event_track/static/src/scss/pwa_frontend.scss"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
<script type="text/javascript" src="/website_event_track/static/src/js/website_event_track_set_customize_options.js"></script>
<script type="text/javascript" src="/website_event_track/static/src/js/event_track_reminder.js"></script>
<script type="text/javascript" src="/website_event_track/static/src/js/website_event_pwa_widget.js"/>
<script type="text/javascript" src="/website_event_track/static/lib/idb-keyval/idb-keyval.js"/>
</xpath>
</template>
@@ -22,6 +22,12 @@
<field name="sponsor_count" string="Sponsors" widget="statinfo"/>
</button>
</xpath>
<xpath expr="//field[@name='website_menu']" position="after">
<label for="website_track" string="Showcase Tracks"/>
<field name="website_track"/>
<label for="website_track_proposal" string="Allow Track Proposals"/>
<field name="website_track_proposal"/>
</xpath>
</field>
</record>
@@ -24,6 +24,13 @@
groups="base.group_no_one"
sequence="32"/>
<menuitem id="event_track_tag_category_menu"
name="Track Tag Categories"
action="event_track_tag_category_action"
parent="event.menu_event_configuration"
groups="base.group_no_one"
sequence="33"/>
<menuitem
id="menu_event_track_tag"
name="Track Tags"
@@ -32,6 +39,13 @@
groups="base.group_no_one"
sequence="34"/>
<menuitem id="event_track_visitor_menu"
name="Track Visitors"
sequence="38"
action="event_track_visitor_action"
parent="event.menu_event_configuration"
groups="base.group_no_one"/>
<menuitem id="menu_event_sponsor_type"
name="Sponsor Types"
action="event_sponsor_type_action"
@@ -10,6 +10,7 @@
<sheet>
<group>
<field name="name"/>
<field name="display_ribbon_style"/>
<field name="sequence" groups="base.group_no_one"/>
</group>
</sheet>
@@ -24,6 +25,7 @@
<tree editable="bottom" string="Event Sponsor Type">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="display_ribbon_style"/>
</tree>
</field>
</record>
@@ -40,6 +42,9 @@
<search string="Event Sponsors">
<field name="partner_id"/>
<field name="event_id"/>
<field name="name"/>
<field name="email"/>
<field name="phone"/>
<filter string="Archived" name="archived" domain="[('active', '=', False)]"/>
<group string="Group By" expand="0">
<filter string="Event" name="group_by_event_id" domain="[]" context="{'group_by': 'event_id'}"/>
@@ -57,18 +62,24 @@
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="active" invisible="1"/>
<field name="image_128" widget="image" class="oe_avatar"/>
<h1><field name="partner_id"/></h1>
<field name="image_512" widget="image" class="oe_avatar"/>
<div class="oe_title">
<label for="name" class="oe_edit_only" string="Sponsor Name"/>
<h1><field name="name" placeholder="e.g. : OpenWood Decoration"/></h1>
</div>
<group>
<group>
<field name="event_id"/>
<field name="partner_email" widget="email"/>
<field name="url" widget="url" string="Website"/>
<field name="partner_id" string="Partner"/>
<field name="email" widget="email" string="Email"
placeholder="e.g. : open.wood.decoration@example.com"/>
<field name="url" widget="url" string="Website"
placeholder=">e.g. : https://www.odoo.com"/>
</group>
<group>
<field name="sponsor_type_id"/>
<field name="partner_phone" widget="phone" options="{'enable_sms': True}"/>
<field name="partner_mobile" widget="phone" options="{'enable_sms': True}"/>
<field name="phone" widget="phone" string="Phone" options="{'enable_sms': True}"/>
<field name="mobile" widget="phone" string="Mobile" options="{'enable_sms': True}"/>
</group>
</group>
</sheet>
@@ -85,9 +96,13 @@
<field name="name">event.sponsor.view.tree</field>
<field name="model">event.sponsor</field>
<field name="arch" type="xml">
<tree>
<tree multi_edit="1">
<field name="sequence" widget="handle"/>
<field name="partner_id"/>
<field name="partner_id" readonly="1"/>
<field name="name"/>
<field name="email"/>
<field name="phone"/>
<field name="mobile"/>
<field name="url" string="Website"/>
<field name="sponsor_type_id"/>
</tree>
@@ -133,5 +148,6 @@
</p>
</field>
</record>
</data>
</odoo>
@@ -20,7 +20,7 @@
<t t-set="pageName" t-value="'event'"/>
<t t-set="head">
<t t-raw="head"/>
<t t-call="website_event_track_online.pwa_manifest"/>
<t t-call="website_event_track.pwa_manifest"/>
</t>
</xpath>
</template>
@@ -30,7 +30,7 @@
<t t-set="pageName" t-value="'event'"/>
<t t-set="head">
<t t-raw="head"/>
<t t-call="website_event_track_online.pwa_manifest"/>
<t t-call="website_event_track.pwa_manifest"/>
</t>
</xpath>
</template>
@@ -26,6 +26,7 @@
</group>
<group>
<field name="fold"/>
<field name="is_accepted"/>
<field name="is_done"/>
<field name="is_cancel"/>
<field name="color" widget="color_picker"/>
@@ -43,6 +44,9 @@
<tree string="Track Stage">
<field name="sequence" widget="handle" groups="event.group_event_manager"/>
<field name="name"/>
<field name="is_accepted"/>
<field name="is_done"/>
<field name="is_cancel"/>
<field name="fold"/>
</tree>
</field>
@@ -1,7 +1,44 @@
<?xml version="1.0"?>
<odoo>
<record id="event_track_tag_category_view_form" model="ir.ui.view">
<field name="name">event.track.tag.category.view.form</field>
<field name="model">event.track.tag.category</field>
<field name="arch" type="xml">
<form string="Track Tag Category">
<sheet>
<group>
<field name="name"/>
<field name="tag_ids" context="{'default_category_id': active_id}">
<tree string="Tags" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="color" widget="color_picker"/>
</tree>
</field>
</group>
</sheet>
</form>
</field>
</record>
<record id="event_track_tag_category_view_list" model="ir.ui.view">
<field name="name">event.track.tag.category.view.list</field>
<field name="model">event.track.tag.category</field>
<field name="arch" type="xml">
<tree string="Track Tags Category">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</tree>
</field>
</record>
<record id="event_track_tag_category_action" model="ir.actions.act_window">
<field name="name">Track Tag Categories</field>
<field name="res_model">event.track.tag.category</field>
<field name="view_mode">tree,form</field>
</record>
<!-- EVENTS/CONFIGURATION/EVENT Tags -->
<record model="ir.ui.view" id="view_event_track_tag_form">
<field name="name">Track Tags</field>
<field name="model">event.track.tag</field>
@@ -10,6 +47,8 @@
<sheet>
<group>
<field name="name"/>
<field name="color" widget="color_picker"/>
<field name="category_id"/>
</group>
</sheet>
</form>
@@ -21,7 +60,10 @@
<field name="model">event.track.tag</field>
<field name="arch" type="xml">
<tree string="Event Track Tag" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="color" widget="color_picker"/>
<field name="category_id"/>
</tree>
</field>
</record>
@@ -1,76 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="agenda">
<!-- Revamped agenda : Will need to replace agenda_online from website_event_track in master -->
<template id="agenda_online" name="Track Online: Agenda">
<t t-call="website_event.layout">
<div class="oe_structure" id="oe_structure_website_event_track_agenda_1"/>
<section class="container pt48">
<h1 t-field="event.name"/>
<div class="form-inline float-right">
<label class="invisible text-muted" id="search_summary"><span id="search_number">0</span> Found </label>
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
<div class="o_wevent_online o_weagenda_index">
<!-- Options -->
<t t-set="option_track_wishlist" t-value="not event.is_done and request.website.viewref('website_event_track.agenda_topbar_wishlist').active"/>
<!-- Topbar -->
<t t-call="website_event_track.agenda_topbar"/>
<!-- Drag/Drop Area -->
<div class="oe_structure" id="oe_structure_website_event_track_agenda_1"/>
<!-- Content -->
<div class="container">
<div class="row mb-5">
<t t-call="website_event_track.agenda_main"/>
</div>
</div>
</section>
<t t-set="dayslist" t-value="list(days.keys())"/>
<t t-set="dayslist2" t-value="dayslist.sort()"/> <!-- display days in the right order -->
<section class="container" t-foreach="dayslist" t-as="day">
<t t-set="locations" t-value="days[day]['locations']"/>
<t t-set="dates" t-value="days[day]['dates']"/>
<h3 class="o_page_header mt0">
<span t-field="tracks_by_days[day][0].date" t-options="{'widget': 'date'}"/>
<small><t t-esc="len(tracks_by_days[day])"/> talks</small>
</h3>
<table id="table_search" class="table table-bordered table-sm">
<!-- Drag/Drop Area -->
<div class="oe_structure" id="oe_structure_website_event_track_agenda_2"/>
</div>
</t>
</template>
<!-- ============================================================ -->
<!-- TOPBAR: BASE NAVIGATION -->
<!-- ============================================================ -->
<!-- Main topbar -->
<template id="agenda_topbar" name="Agenda Tools">
<nav class="navbar navbar-light border-top shadow-sm d-print-none">
<div class="container">
<div class="d-flex flex-column flex-sm-row justify-content-between w-100">
<ul class="o_weagenda_topbar_filters o_wevent_index_topbar_filters nav">
</ul>
<div class="form-inline pl-sm-3 pr-0">
<label class="invisible text-muted mr-2" id="search_summary"><span id="search_number" class="mr-1">0</span>Results</label>
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
</div>
</div>
</div>
</nav>
</template>
<!-- Option: Tracks display: optional wishlist -->
<template id="agenda_topbar_wishlist"
inherit_id="website_event_track.agenda_topbar"
name="Allow Wishlists"
active="True"
customize_show="True">
<xpath expr="//ul[hasclass('o_weagenda_topbar_filters')]" position="inside">
</xpath>
</template>
<!-- ============================================================ -->
<!-- CONTENT: MAIN TEMPLATES -->
<!-- ============================================================ -->
<!-- Agenda Main Display -->
<template id="agenda_main" name="Tracks: Main Display">
<!-- No tracks -->
<div class="col-12" t-if="not tracks_by_days">
<div class="h2 mb-3">No track found.</div>
<div t-if="search_key" class="alert alert-info text-center">
<p class="m-0">We did not find any track matching your <strong t-esc="search_key"/> search.</p>
</div>
<div t-else="" class="alert alert-info text-center" groups="event.group_event_manager">
<p class="m-0">Schedule some tracks to get started !</p>
</div>
</div>
<section t-else="" class="col-12" t-foreach="days" t-as="day">
<!-- DAY HEADER -->
<div class="o_we_track_day_header mt-3 w-100 d-flex justify-content-between align-items-center">
<div class="d-flex">
<span class="h1 m-0 font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'EEEE dd'}"/>
<div class="d-flex flex-column ml-2">
<span class="font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'MMMM'}"/>
<span class="font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'YYYY'}"/>
</div>
</div>
<small class="float-right text-muted align-self-end"><t t-esc="tracks_by_days[day]"/> tracks</small>
</div>
<hr class="mt-2 pb-1 mb-1"/>
<!-- Day Agenda -->
<div class="o_we_online_agenda">
<table id="table_search" class="table table-sm border-0">
<!--Header-->
<tr>
<th/>
<t t-foreach="locations.keys()" t-as="location">
<th t-if="location" class="active">
<th class="border-0 bg-white position-sticky"/>
<t t-foreach="locations" t-as="location">
<th t-if="location" class="active text-center">
<span t-esc="location and location.name or 'Unknown'"/>
</th>
</t>
</tr>
<tr t-foreach="dates" t-as="dt">
<td class="active">
<b t-esc="dt[-1]"/>
</td>
<t t-if="dt[2]"> <!-- Not a multi location -->
<!-- Time Slots -->
<t t-set="used_cells" t-value="[]"/>
<t t-foreach="time_slots[day]" t-as="time_slot">
<t t-set="is_round_hour" t-value="time_slot == time_slot.replace(minute=0)"/>
<t t-set="is_half_hour" t-value="time_slot == time_slot.replace(minute=30)"/>
<tr t-att-class="'%s' % ('active' if is_round_hour else '')">
<td class="active">
<b t-if="is_round_hour" t-esc="time_slots[day][time_slot]['formatted_time']"/>
</td>
<t t-foreach="locations" t-as="location">
<t t-if="location and dt[1].get(location, False)">
<t t-set="track" t-value="dt[1][location][0]"/>
<t t-if="track">
<td t-att-rowspan="dt[1][location][3]" t-attf-class="event_color_#{track.color} #{track and 'event_track' or ''}">
<t t-if="track" name="track_agenda_track_cell">
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }">
<span t-esc="track and track.name"/>
<span t-if="not track.website_published" class="badge badge-warning">unpublished</span>
</a>
<div class="text-muted">
<small t-if="track.partner_id" t-esc="track.partner_id.sudo().name"/>
<small t-if="not track.partner_id" t-esc="track.partner_name"/>
</div>
</t>
</td>
<t t-set="tracks" t-value="time_slots[day][time_slot].get(location, {})"/>
<t t-if="tracks">
<t t-foreach="tracks" t-as="track">
<t t-if="track.location_id and track.location_id == location">
<td t-att-rowspan="tracks[track]['rowspan']"
t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''} o_location_size_#{len(locations)}">
<t t-call="website_event_track.agenda_main_track"/>
</td>
</t>
<t t-else="">
<td t-att-colspan="len(locations)-1"
t-att-rowspan="tracks[track]['rowspan']"
t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''} o_location_size_#{len(locations)}">
<t t-call="website_event_track.agenda_main_track"/>
</td>
</t>
<t t-set="used_cells" t-value="used_cells + tracks[track]['occupied_cells']"/>
</t>
<td t-if="not track" t-att-rowspan="dt[1][location][3]" class="event_track"/>
</t>
<t t-elif="location and (time_slot, location) not in used_cells">
<td t-att-rowspan="1"
t-att-class="'o_location_size_%s %s' % (len(locations),
'o_we_agenda_time_slot_half' if is_half_hour else
'o_we_agenda_time_slot_main' if is_round_hour else
''
)"><div/></td>
</t>
</t>
</t><t t-if="not dt[2]">
<t t-set="track" t-value="dt[1][False][0]"/>
<td t-att-colspan="len(locations)-1" t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''}">
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }">
<span t-esc="track.name"/>
</a>
<div class="text-muted">
<small t-if="track.partner_id" t-esc="track.partner_id.sudo().name"/>
<small t-if="not track.partner_id" t-esc="track.partner_name"/>
</div>
</td>
</tr>
</t>
</table>
</div>
</section>
</template>
<template id="agenda_main_track" name="Track Agenda: Track">
<div class="d-flex flex-column h-100">
<div class="d-flex justify-content-end flex-wrap-reverse o_weagenda_track_badges">
<small t-if="not track.website_published and user_event_manager and track.stage_id.is_accepted"
title="Unpublished"
class="ml-1 badge badge-danger o_wevent_online_badge_unpublished">Unpublished</small>
<small t-if="not track.stage_id.is_accepted" title="Not Accepted"
class="ml-1 badge badge-danger o_wevent_online_badge_unpublished">Not Accepted</small>
<span t-if="option_track_wishlist">
<t t-call="website_event_track.track_widget_reminder">
<t t-set="reminder_light" t-value="True"/>
<t t-set="reminder_small" t-value="True"/>
<t t-set="light_theme" t-value="False"/>
</t>
</tr>
</table>
</section>
<div class="oe_structure" id="oe_structure_website_event_track_agenda_2"/>
</t>
</span>
</div>
<div class="o_we_agenda_card_content d-flex flex-column justify-content-center my-1">
<div t-att-class="'text-black' if track.website_published or user_event_manager else 'text-muted'"
t-att-onclick="'window.location=\'/event/%s/track/%s\'' % (slug(event), slug(track))
if track.website_published or user_event_manager else ''">
<span class="o_we_agenda_card_title text-bold" t-esc="track.name"/>
</div>
<div class="text-muted text-center">
<small t-if="track.partner_id or track.partner_name">
<t t-esc="track.partner_id.sudo().name or track.partner_name"/></small>
</div>
<div class="d-flex justify-content-center flex-wrap">
<t t-foreach="track.tag_ids" t-as="tag">
<span t-if="tag.color" t-att-title="tag.name"
t-attf-class="mr-1 mt-1 badge #{'o_tag_color_'+str(tag.color)}" t-esc="tag.name"
t-attf-onclick="
var value = '#{tag.name}' ;
var target = $('#event_track_search');
if (target.val() == value) { target.val(''); } else { target.val(value); }
target.trigger('input');
"
/>
</t>
</div>
</div>
</div>
</template>
</odoo>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="tracks">
<template id="tracks" track="True">
<t t-call="website_event.layout">
<div class="oe_structure" id="oe_structure_website_event_track_tracks_1"/>
<section class="pt48 pb48">
@@ -76,4 +76,18 @@
</xpath>
</template>
<!-- Add wishlist button on tracks / track templates -->
<template id="tracks_wishlist"
name="Wishlist on Tracks"
inherit_id="website_event_track.tracks"
customize_show="True">
<xpath expr="//h3" position="replace">
<h3 class="mt0 mb0 d-flex">
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }" class="text-secondary"><span t-field="track.name"> </span></a>
<small t-if="not track.website_published" class="badge badge-danger">unpublished</small>
<t t-call="website_event_track.track_widget_reminder"/>
</h3>
</xpath>
</template>
</odoo>
@@ -4,26 +4,31 @@
<template name="Sponsors" id="event_sponsor" customize_show="True" inherit_id="website_event.layout">
<xpath expr="//div[@id='wrap']" position="inside">
<div class="container mt32 mb16 d-print-none" t-if="event.sponsor_ids">
<section>
<h2 class="text-center mb32">Our Sponsors</h2>
</section>
<div class="row">
<div t-attf-class="col-sm-6 col-lg-#{(len(event.sponsor_ids) > 6) and 2 or (12 // len(event.sponsor_ids))} text-center mb16 oe_sponsor" t-foreach="event.sponsor_ids" t-as="sponsor">
<div t-attf-class="d-flex flex-wrap mb-5 #{'' if (len(event.sponsor_ids) > 10) else 'justify-content-md-center'}">
<t t-foreach="event.sponsor_ids" t-as="sponsor">
<t t-if="sponsor.url">
<a t-att-href="sponsor.url" class="bg-white o_wevent_sponsor">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "shadow"}'/>
<span t-field="sponsor.sponsor_type_id" t-attf-class="o_ribbon o_ribbon_right ribbon_#{sponsor.sponsor_type_id.name}"/>
<a class="o_wevent_sponsor_card" target="_blank" t-att-href="sponsor.url">
<div class="h-100 shadow-sm p-2">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "img img-fluid"}'/>
<div t-if="sponsor.sponsor_type_id.display_ribbon_style and sponsor.sponsor_type_id.display_ribbon_style != 'no_ribbon'" class="ribbon-wrapper">
<div t-field="sponsor.sponsor_type_id" t-attf-class="ribbon ribbon_#{sponsor.sponsor_type_id.display_ribbon_style}"/>
</div>
</div>
</a>
</t>
<t t-if="not sponsor.url">
<span class="bg-white o_wevent_sponsor">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "shadow"}'/>
<span t-field="sponsor.sponsor_type_id" t-attf-class="o_ribbon o_ribbon_right ribbon_#{sponsor.sponsor_type_id.name}"/>
</span>
<div class="o_wevent_sponsor_card">
<div class="h-100 shadow-sm p-2">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "img img-fluid"}'/>
<div t-if="sponsor.sponsor_type_id.display_ribbon_style and sponsor.sponsor_type_id.display_ribbon_style != 'no_ribbon'" class="ribbon-wrapper">
<div t-field="sponsor.sponsor_type_id" t-attf-class="ribbon ribbon_#{sponsor.sponsor_type_id.display_ribbon_style}"/>
</div>
</div>
</div>
</t>
</div>
</t>
</div>
</div>
</xpath>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="track_view">
<template id="track_view" track="True">
<t t-call="website_event.layout">
<section class="pt48">
<div class="container">
@@ -82,4 +82,18 @@
</t>
</template>
<template id="track_view_wishlist"
name="Wishlist on Track"
inherit_id="website_event_track.track_view"
customize_show="True">
<xpath expr="//h1" position="replace">
<div class="d-flex">
<h1 t-field="track.name"/>
<div class="ml-2 pt-1">
<t t-call="website_event_track.track_widget_reminder"/>
</div>
</div>
</xpath>
</template>
</odoo>
@@ -1,234 +1,268 @@
<?xml version="1.0"?>
<odoo>
<record id="website_visitor_action_from_track" model="ir.actions.act_window">
<field name="name">Visitors Wishlist</field>
<field name="res_model">website.visitor</field>
<field name="view_mode">kanban,tree,form,graph</field>
<field name="domain">[('event_track_wishlisted_ids', 'in', [active_id])]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Wait for visitors to wishlist your tracks
</p>
</field>
</record>
<!-- Event Tracks -->
<record model="ir.ui.view" id="view_event_track_kanban">
<field name="name">event.track.kanban</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<templates>
<field name="color"/>
<field name="partner_id"/>
<field name="stage_id"/>
<field name="website_url"/>
<field name="activity_ids"/>
<field name="activity_state"/>
<progressbar field="kanban_state" colors='{"done": "success", "blocked": "danger"}'/>
<t t-name="kanban-box">
<div t-attf-class="{{!selection_mode ? 'oe_kanban_color_' + kanban_getcolor(record.color.raw_value) : ''}} oe_kanban_card oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown" groups="base.group_user">
<record model="ir.ui.view" id="view_event_track_kanban">
<field name="name">event.track.kanban</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<templates>
<field name="color"/>
<field name="partner_id"/>
<field name="stage_id"/>
<field name="website_url"/>
<field name="activity_ids"/>
<field name="activity_state"/>
<progressbar field="kanban_state" colors='{"done": "success", "blocked": "danger"}'/>
<t t-name="kanban-box">
<div t-attf-class="{{!selection_mode ? 'oe_kanban_color_' + kanban_getcolor(record.color.raw_value) : ''}} oe_kanban_card oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown" groups="base.group_user">
<a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<a role="menuitem" t-att-href="record.website_url.value" class="dropdown-item">View Track</a>
<t t-if="widget.editable"><a role="menuitem" type="edit" class="dropdown-item">Edit Track</a></t>
<t t-if="widget.deletable"><a role="menuitem" type="delete" class="dropdown-item">Delete</a></t>
<ul class="oe_kanban_colorpicker" data-field="color"/>
</div>
<a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<a role="menuitem" t-att-href="record.website_url.value" class="dropdown-item">View Track</a>
<t t-if="widget.editable"><a role="menuitem" type="edit" class="dropdown-item">Edit Track</a></t>
<t t-if="widget.deletable"><a role="menuitem" type="delete" class="dropdown-item">Delete</a></t>
<ul class="oe_kanban_colorpicker" data-field="color"/>
</div>
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<h4 class="o_kanban_record_title"><field name="name"/></h4>
</div>
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<h4 class="o_kanban_record_title"><field name="name"/></h4>
</div>
<div class="o_kanban_record_body">
<t t-if="duration"><field name="duration" widget="float_time"/> hours</t>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<field name="priority" widget="priority"/>
<field name="activity_ids" widget="kanban_activity"/>
</div>
<div class="o_kanban_record_body">
<t t-if="duration"><field name="duration" widget="float_time"/> hours</t>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<field name="priority" widget="priority"/>
<field name="activity_ids" widget="kanban_activity"/>
</div>
<div class="oe_kanban_bottom_right">
<field name="kanban_state" widget="state_selection" groups="base.group_user"/>
<img t-att-src="kanban_image('res.partner', 'image_128', record.partner_id.raw_value)"
t-att-title="record.partner_id.value" t-att-alt="record.partner_id.value"
class="oe_kanban_avatar"/>
</div>
<div class="oe_kanban_bottom_right">
<field name="kanban_state" widget="state_selection" groups="base.group_user"/>
<img t-att-src="kanban_image('res.partner', 'image_128', record.partner_id.raw_value)"
t-att-title="record.partner_id.value" t-att-alt="record.partner_id.value"
class="oe_kanban_avatar"/>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="view_event_track_calendar" model="ir.ui.view">
<field name="name">event.track.calendar</field>
<field name="model">event.track</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date" date_delay="duration" string="Event Tracks" color="location_id" event_limit="5">
<field name="location_id" filters="1"/>
<field name="event_id"/>
<field name="partner_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="image_128"/>
</calendar>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_search">
<field name="name">event.track.search</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<search string="Event Tracks">
<field name="name"/>
<field name="event_id"/>
<field name="location_id"/>
<field name="stage_id"/>
<field name="partner_id"/>
<filter string="My Tracks" name="my_tracks" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction', '=', True)]"/>
<separator/>
<filter name="filter_date" date="date"/>
<separator/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<separator/>
<filter invisible="1" string="Late Activities" name="activities_overdue"
domain="[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]"
help="Show all records which has next action date is before today"/>
<filter invisible="1" string="Today Activities" name="activities_today"
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
domain="[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]"/>
<group expand="0" string="Group By">
<filter string="Responsible" name="responsible" context="{'group_by': 'user_id'}"/>
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
<filter string="Date" name="date" context="{'group_by': 'date'}"/>
<filter string="Event" name="event" context="{'group_by': 'event_id'}"/>
<filter string="Location" name="location" context="{'group_by': 'location_id'}"/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_form">
<field name="name">event.track.form</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<form string="Event Track">
<header>
<field name="stage_id" widget="statusbar" options="{'clickable': '1'}"/>
</header>
<sheet string="Track">
<div class="oe_button_box" name="button_box">
<field name="website_url" invisible="1"/>
<field name="is_published" widget="website_redirect_button"/>
</div>
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="kanban_state" widget="state_selection" class="ml-3 float-right"/>
<field name="image" widget="image" class="oe_avatar"/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="e.g. Inspiring Business Talk"/>
</h1>
</div>
<group>
<group>
<field name="date"/>
<field name="location_id"/>
<label for="duration"/>
<div class="o_row">
<field name="duration" widget="float_time"/>
<span>hours</span>
</div>
<field name="active" invisible="1"/>
</group>
<group>
<field name="company_id" invisible="1"/>
<field name="user_id" domain="[('share', '=', False)]"/>
<field name="event_id"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="color" widget="color_picker"/>
</group>
</group>
<notebook>
<page string="Speakers" name="speakers">
<group>
<field name="partner_id"
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"
context="{'default_phone': partner_phone, 'default_email': partner_email}"/>
<field name="partner_name"/>
<field name="partner_email"/>
<field name="partner_phone" class="o_force_ltr"/>
<field name="partner_biography"/>
</group>
</page>
<page string="Description" name="description">
<field name="description"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids" options="{'post_refresh': 'recipients'}"/>
</t>
</templates>
</kanban>
</field>
</record>
<record id="view_event_track_calendar" model="ir.ui.view">
<field name="name">event.track.calendar</field>
<field name="model">event.track</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date" date_delay="duration" string="Event Tracks" color="location_id" event_limit="5">
<field name="location_id" filters="1"/>
<field name="event_id"/>
<field name="partner_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="image_128"/>
</calendar>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_search">
<field name="name">event.track.search</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<search string="Event Tracks">
<field name="name"/>
<field name="event_id"/>
<field name="location_id"/>
<field name="stage_id"/>
<field name="partner_id"/>
<filter string="My Tracks" name="my_tracks" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction', '=', True)]"/>
<separator/>
<filter string="Always Wishlisted" name="filter_wishlisted_by_default" domain="[('wishlisted_by_default', '=', True)]"/>
<separator/>
<filter name="filter_date" date="date"/>
<separator/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<separator/>
<filter invisible="1" string="Late Activities" name="activities_overdue"
domain="[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]"
help="Show all records which has next action date is before today"/>
<filter invisible="1" string="Today Activities" name="activities_today"
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
domain="[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]"/>
<group expand="0" string="Group By">
<filter string="Responsible" name="responsible" context="{'group_by': 'user_id'}"/>
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
<filter string="Date" name="date" context="{'group_by': 'date'}"/>
<filter string="Event" name="event" context="{'group_by': 'event_id'}"/>
<filter string="Location" name="location" context="{'group_by': 'location_id'}"/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_form">
<field name="name">event.track.form</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<form string="Event Track">
<header>
<field name="stage_id" widget="statusbar" options="{'clickable': '1'}"/>
</header>
<sheet string="Track">
<div class="oe_button_box" name="button_box">
<field name="website_url" invisible="1"/>
<button name="%(website_event_track.website_visitor_action_from_track)d"
type="action"
class="oe_stat_button"
icon="fa-bell">
<field name="wishlist_visitor_count" string="Wishlisted By" widget="statinfo"/>
</button>
<field name="is_published" widget="website_redirect_button"/>
</div>
</form>
</field>
</record>
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="kanban_state" widget="state_selection" class="ml-3 float-right"/>
<field name="website_image" widget="image" class="oe_avatar"/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="e.g. Inspiring Business Talk"/>
</h1>
</div>
<group>
<group>
<field name="date"/>
<field name="location_id"/>
<label for="duration"/>
<div class="o_row">
<field name="duration" widget="float_time"/>
<span>hours</span>
</div>
<field name="active" invisible="1"/>
<field name="wishlisted_by_default"/>
</group>
<group>
<field name="company_id" invisible="1"/>
<field name="user_id" domain="[('share', '=', False)]"/>
<field name="event_id"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="color" widget="color_picker"/>
</group>
</group>
<notebook>
<page string="Speakers" name="speakers">
<group>
<field name="partner_id"
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"
context="{'default_phone': partner_phone, 'default_email': partner_email}"/>
<field name="partner_name"/>
<field name="partner_email"/>
<field name="partner_phone" class="o_force_ltr"/>
<field name="partner_biography"/>
<field name="image" widget="image" class="oe_avatar float-left"/>
</group>
</page>
<page string="Description" name="description">
<field name="description"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids" options="{'post_refresh': 'recipients'}"/>
</div>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_tree">
<field name="name">event.track.tree</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<tree string="Event Track">
<field name="name"/>
<field name="active" invisible="1"/>
<field name="partner_name"/>
<field name="partner_id" optional="hide"/>
<field name="partner_email"/>
<field name="partner_phone"/>
<field name="event_id" invisible="context.get('default_event_id')"/>
<field name="stage_id"/>
<field name="color" widget="color_picker" optional="hide"/>
<field name="activity_exception_decoration" widget="activity_exception"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_tree">
<field name="name">event.track.tree</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<tree string="Event Track">
<field name="name"/>
<field name="active" invisible="1"/>
<field name="partner_name"/>
<field name="partner_id" optional="hide"/>
<field name="partner_email"/>
<field name="partner_phone"/>
<field name="event_id" invisible="context.get('default_event_id')"/>
<field name="wishlisted_by_default" optional="hide"/>
<field name="wishlist_visitor_count" optional="hide"/>
<field name="stage_id"/>
<field name="color" widget="color_picker" optional="hide"/>
<field name="activity_exception_decoration" widget="activity_exception"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_graph">
<field name="name">event.track.graph</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<graph string="Tracks" type="bar" orientation="horizontal" sample="1">
<field name="location_id"/>
<field name="duration" operator="+"/>
</graph>
</field>
</record>
<record model="ir.ui.view" id="view_event_track_graph">
<field name="name">event.track.graph</field>
<field name="model">event.track</field>
<field name="arch" type="xml">
<graph string="Tracks" type="bar" orientation="horizontal" sample="1">
<field name="location_id"/>
<field name="duration" operator="+"/>
</graph>
</field>
</record>
<record model="ir.actions.act_window" id="action_event_track">
<field name="name">Event Tracks</field>
<field name="res_model">event.track</field>
<field name="view_mode">kanban,tree,form,calendar,graph,activity</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new track
</p><p>
Tracks define the schedule of your event.<br/>These can be a talk, a round table, a meeting, etc.
</p>
</field>
</record>
<record model="ir.actions.act_window" id="action_event_track">
<field name="name">Event Tracks</field>
<field name="res_model">event.track</field>
<field name="view_mode">kanban,tree,form,calendar,graph,activity</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new track
</p><p>
Tracks define the schedule of your event.<br/>These can be a talk, a round table, a meeting, etc.
</p>
</field>
</record>
<record model="ir.actions.act_window" id="action_event_track_from_event">
<field name="res_model">event.track</field>
<field name="name">Event Tracks</field>
<field name="view_mode">kanban,tree,form,calendar,graph,activity</field>
<field name="context">{'search_default_event_id': active_id, 'default_event_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new track
</p><p>
Tracks define the schedule of your event.<br/>These can be a talk, a round table, a meeting, etc.
</p>
</field>
</record>
<record model="ir.actions.act_window" id="action_event_track_from_event">
<field name="res_model">event.track</field>
<field name="name">Event Tracks</field>
<field name="view_mode">kanban,tree,form,calendar,graph,activity</field>
<field name="context">{'search_default_event_id': active_id, 'default_event_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new track
</p><p>
Tracks define the schedule of your event.<br/>These can be a talk, a round table, a meeting, etc.
</p>
</field>
</record>
<record id="event_track_action_from_visitor" model="ir.actions.act_window">
<field name="name">Wishlisted Tracks</field>
<field name="res_model">event.track</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('wishlist_visitor_ids', 'in', [active_id])]</field>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
No track wishlisted by this visitor
</p>
</field>
</record>
</odoo>
@@ -18,7 +18,7 @@
<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"
<button name="%(website_event_track.event_track_action_from_visitor)d"
type="action"
class="oe_stat_button" icon="fa-ticket"
attrs="{'invisible': [('event_track_wishlisted_count', '=', 0)]}">
@@ -11,7 +11,7 @@
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
'website_event_track_online',
'website_event_track',
'website_jitsi',
],
'data': [
@@ -6,12 +6,12 @@ from random import randint, sample
from werkzeug.exceptions import NotFound, Forbidden
from odoo import exceptions, http
from odoo.addons.website_event_track.controllers.main import WebsiteEventTrackController
from odoo.addons.website_event_track.controllers.event_track import EventTrackController
from odoo.http import request
from odoo.osv import expression
class ExhibitorController(WebsiteEventTrackController):
class ExhibitorController(EventTrackController):
def _get_event_sponsors_base_domain(self, event):
search_domain_base = [
@@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date, datetime, timedelta
from datetime import datetime
from unittest.mock import patch
from odoo.addons.website_event_track_online.tests.common import TestEventTrackOnlineCommon
from odoo.addons.website_event_track.tests.common import TestEventTrackOnlineCommon
from odoo.fields import Datetime as FieldsDatetime
from odoo.tests.common import users
@@ -18,6 +20,14 @@ class TestSponsorData(TestEventTrackOnlineCommon):
'hour_to': 18.0,
})
cls.wevent_exhib_dt = patch(
'odoo.addons.website_event_track_exhibitor.models.event_sponsor.fields.Datetime',
wraps=FieldsDatetime
)
cls.mock_wevent_exhib_dt = cls.wevent_exhib_dt.start()
cls.mock_wevent_exhib_dt.now.return_value = cls.reference_now
cls.addClassCleanup(cls.wevent_exhib_dt.stop)
@users('user_eventmanager')
def test_event_date_computation(self):
""" Test date computation. Pay attention that mocks returns UTC values, meaning
@@ -29,42 +39,48 @@ class TestSponsorData(TestEventTrackOnlineCommon):
self.assertTrue(event.is_ongoing)
# After hour_from (9 > 8)
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 6, 7, 0, 0)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 6, 7, 0, 0)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 6, 7, 0, 0)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertTrue(sponsor.is_in_opening_hours)
self.assertTrue(event.is_ongoing)
# At hour_from (8 = 8)
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 6, 6, 0, 0)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 6, 6, 0, 0)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 6, 6, 0, 0)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertTrue(sponsor.is_in_opening_hours)
self.assertTrue(event.is_ongoing)
# Started but not opened (7h59 < 8)
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 6, 5, 59, 59)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 6, 5, 59, 59)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 6, 5, 59, 59)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertFalse(sponsor.is_in_opening_hours)
self.assertTrue(event.is_ongoing)
# Evening event is not in opening hours (20 > 18)
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 6, 18, 0, 0)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 6, 18, 0, 0)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 6, 18, 0, 0)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertFalse(sponsor.is_in_opening_hours)
self.assertTrue(event.is_ongoing)
# First day begins later
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 5, 6, 30, 0)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 5, 6, 30, 0)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 5, 6, 30, 0)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertFalse(sponsor.is_in_opening_hours)
self.assertFalse(event.is_ongoing)
# End day finished sooner
self.mock_event_online_dt.now.return_value = datetime(2020, 7, 7, 13, 0, 1)
self.mock_wevent_dt.now.return_value = datetime(2020, 7, 7, 13, 0, 1)
self.mock_wevent_exhib_dt.now.return_value = datetime(2020, 7, 7, 13, 0, 1)
event.invalidate_cache(fnames=['is_ongoing'])
sponsor.invalidate_cache(fnames=['is_in_opening_hours'])
self.assertFalse(sponsor.is_in_opening_hours)
@@ -2,7 +2,7 @@
<odoo>
<record id="event_event_view_form" model="ir.ui.view">
<field name="name">event.event.view.form.inherit.exhibitor</field>
<field name="inherit_id" ref="website_event_track_online.event_event_view_form"/>
<field name="inherit_id" ref="website_event_track.event_event_view_form"/>
<field name="model">event.event</field>
<field name="priority" eval="3"/>
<field name="arch" type="xml">
@@ -3,7 +3,7 @@
<record id="event_sponsor_view_search" model="ir.ui.view">
<field name="name">event.sponsor.view.search.inherit.exhibitor</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track_online.event_sponsor_view_search"/>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='archived']" position="after">
<separator/>
@@ -15,7 +15,7 @@
<record id="event_sponsor_view_tree" model="ir.ui.view">
<field name="name">event.sponsor.view.tree.inherit.exhibitor</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track_online.event_sponsor_view_tree"/>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='sponsor_type_id']" position="after">
<field name="is_published" optional="show"/>
@@ -38,7 +38,7 @@
<record id="event_sponsor_view_form" model="ir.ui.view">
<field name="name">event.sponsor.view.form.inherit.exhibitor</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track_online.event_sponsor_view_form"/>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//widget[@name='web_ribbon']" position="before">
<div class="oe_button_box" name="button_box">
@@ -11,7 +11,7 @@
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
'website_event_track_online',
'website_event_track',
'website_event_track_session',
],
'data': [
@@ -3,10 +3,10 @@
from odoo import http
from odoo.addons.website_event_track_online.controllers.event_track import EventTrackOnlineController
from odoo.addons.website_event_track.controllers.event_track import EventTrackController
class EventTrackLiveController(EventTrackOnlineController):
class EventTrackLiveController(EventTrackController):
@http.route('/event_track/get_track_suggestion', type='json', auth='public', website=True)
def get_next_track_suggestion(self, track_id):
@@ -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,46 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Tracks Online',
'category': 'Marketing/Events',
'sequence': 1003,
'version': '1.0',
'summary': 'Bridge module to support Online Events Tracks',
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
'sms',
'website_event',
'website_event_track',
],
'data': [
'data/event_sponsor_type_data.xml',
'data/event_track_stage_data.xml',
'security/ir.model.access.csv',
'views/assets.xml',
'views/event_event_views.xml',
'views/event_sponsor_views.xml',
'views/event_sponsor_type_views.xml',
'views/event_templates_registration.xml',
'views/event_track_stage_views.xml',
'views/event_track_templates_reminder.xml',
'views/event_track_templates.xml',
'views/event_track_templates_agenda.xml',
'views/event_track_templates_reminder.xml',
'views/event_track_views.xml',
'views/event_track_tag_views.xml',
'views/event_track_visitor_views.xml',
'views/website_visitor_views.xml',
'views/event_menus.xml',
],
'demo': [
'data/event_sponsor_demo.xml',
'data/event_track_demo.xml',
'data/event_track_tag_demo.xml',
'data/event_track_visitor_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_track
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="website_event_track.event_7_sponsor_0" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_1"/>
</record>
<record id="website_event_track.event_7_sponsor_1" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_2"/>
</record>
<record id="website_event_track.event_7_sponsor_2" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_3"/>
</record>
<record id="website_event_track.event_7_sponsor_3" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_4"/>
</record>
<record id="website_event_track.event_7_sponsor_4" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_10"/>
</record>
<record id="website_event_track.event_7_sponsor_5" model="event.sponsor">
<field name="partner_id" ref="base.res_partner_12"/>
</record>
<record id="website_event_track.event_7_sponsor_6" model="event.sponsor">
<field name="partner_id" ref="event.res_partner_event_1"/>
</record>
<record id="website_event_track.event_7_sponsor_7" model="event.sponsor">
<field name="partner_id" ref="event.res_partner_event_2"/>
</record>
<record id="website_event_track.event_7_sponsor_8" model="event.sponsor">
<field name="partner_id" ref="event.res_partner_event_3"/>
</record>
<record id="website_event_track.event_7_sponsor_9" model="event.sponsor">
<field name="partner_id" ref="event.res_partner_event_4"/>
</record>
</data></odoo>
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="website_event_track.event_sponsor_type1" model="event.sponsor.type">
<field name="display_ribbon_style">Bronze</field>
</record>
<record id="website_event_track.event_sponsor_type2" model="event.sponsor.type">
<field name="display_ribbon_style">Silver</field>
</record>
<record id="website_event_track.event_sponsor_type3" model="event.sponsor.type">
<field name="display_ribbon_style">Gold</field>
</record>
</data></odoo>
@@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="website_event_track.event_7_track_1" model="event.track">
<field name="wishlisted_by_default" eval="True"/>
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="website_event_track.event_7_track_2" model="event.track">
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="website_event_track.event_7_track_3" model="event.track">
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="website_event_track.event_7_track_4" model="event.track">
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="website_event_track.event_7_track_5" model="event.track">
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="website_event_track.event_7_track_6" model="event.track">
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="website_event_track.event_7_track_7" model="event.track">
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<record id="website_event_track.event_7_track_8" model="event.track">
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<record id="website_event_track.event_7_track_l3_1" model="event.track">
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="website_event_track.event_7_track_l3_2" model="event.track">
<field name="partner_id" ref="base.res_partner_address_18"/>
</record>
<record id="website_event_track.event_7_track_10" model="event.track">
<field name="wishlisted_by_default" eval="True"/>
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="website_event_track.event_7_track_11" model="event.track">
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="website_event_track.event_7_track_12" model="event.track">
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="website_event_track.event_7_track_13" model="event.track">
<field name="wishlisted_by_default" eval="True"/>
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="website_event_track.event_7_track_14" model="event.track">
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<record id="website_event_track.event_7_track_15" model="event.track">
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="website_event_track.event_7_track_16" model="event.track">
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="website_event_track.event_7_track_17" model="event.track">
<field name="wishlisted_by_default" eval="True"/>
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
<record id="website_event_track.event_7_track_l3_10" model="event.track">
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="website_event_track.event_7_track_l3_11" model="event.track">
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<record id="website_event_track.event_7_track_20" model="event.track">
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="website_event_track.event_7_track_21" model="event.track">
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<record id="website_event_track.event_7_track_22" model="event.track">
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<record id="website_event_track.event_7_track_23" model="event.track">
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<record id="website_event_track.event_7_track_24" model="event.track">
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
<record id="website_event_track.event_7_track_25" model="event.track">
<field name="partner_id" ref="base.res_partner_address_18"/>
</record>
<record id="website_event_track.event_7_track_26" model="event.track">
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
</data></odoo>
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<record id="website_event_track.event_track_stage2" model="event.track.stage">
<field name="is_accepted" eval="True"/>
</record>
<record id="website_event_track.event_track_stage3" model="event.track.stage">
<field name="is_accepted" eval="True"/>
</record>
</data></odoo>
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="event_track_tag_category_1" model="event.track.tag.category">
<field name="name">Audience</field>
<field name="sequence">10</field>
</record>
<record id="event_track_tag_category_2" model="event.track.tag.category">
<field name="name">Format</field>
<field name="sequence">20</field>
</record>
<record id="website_event_track.event_track_tag1" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_1"/>
<field name="sequence">1</field>
</record>
<record id="website_event_track.event_track_tag2" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_1"/>
<field name="sequence">2</field>
</record>
<record id="website_event_track.event_track_tag3" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_1"/>
<field name="sequence">3</field>
</record>
<record id="website_event_track.event_track_tag11" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_2"/>
<field name="sequence">11</field>
</record>
<record id="website_event_track.event_track_tag12" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_2"/>
<field name="sequence">12</field>
</record>
<record id="website_event_track.event_track_tag13" model="event.track.tag">
<field name="category_id" ref="website_event_track_online.event_track_tag_category_2"/>
<field name="sequence">13</field>
</record>
</data></odoo>
@@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event_sponsor
from . import event_sponsor_type
from . import event_track_visitor
from . import event_track
from . import event_track_stage
from . import event_track_tag
from . import website
from . import website_visitor
@@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.modules.module import get_resource_path
class Sponsor(models.Model):
_name = 'event.sponsor'
_inherit = ['event.sponsor']
# contact information
name = fields.Char('Sponsor Name', compute='_compute_name', readonly=False, store=True)
email = fields.Char('Sponsor Email', compute='_compute_email', readonly=False, store=True)
phone = fields.Char('Sponsor Phone', compute='_compute_phone', readonly=False, store=True)
mobile = fields.Char('Sponsor Mobile', compute='_compute_mobile', readonly=False, store=True)
# image
image_512 = fields.Image(
string="Logo", max_width=512, max_height=512,
compute='_compute_image_512', readonly=False, store=True)
image_256 = fields.Image("Image 256", related="image_512", max_width=256, max_height=256, store=False)
image_128 = fields.Image("Image 128", related="image_512", max_width=128, max_height=128, store=False)
website_image_url = fields.Char(
string='Image URL', max_width=256, max_height=256,
compute='_compute_website_image_url', compute_sudo=True, store=False)
@api.depends('partner_id')
def _compute_name(self):
self._synchronize_with_partner('name')
@api.depends('partner_id')
def _compute_email(self):
self._synchronize_with_partner('email')
@api.depends('partner_id')
def _compute_phone(self):
self._synchronize_with_partner('phone')
@api.depends('partner_id')
def _compute_mobile(self):
self._synchronize_with_partner('mobile')
@api.depends('partner_id')
def _compute_image_512(self):
self._synchronize_with_partner('image_512')
@api.depends('image_256', 'partner_id.image_256')
def _compute_website_image_url(self):
for sponsor in self:
if sponsor.image_256:
sponsor.website_image_url = self.env['website'].image_url(sponsor, 'image_256', size=256)
elif sponsor.partner_id.image_256:
sponsor.website_image_url = self.env['website'].image_url(sponsor.partner_id, 'image_256', size=256)
else:
sponsor.website_image_url = get_resource_path('website_event_track', 'static/src/img', 'event_sponsor_default_%d.png' % (sponsor.id % 1))
def _synchronize_with_partner(self, fname):
""" Synchronize with partner if not set. Setting a value does not write
on partner as this may be event-specific information. """
for sponsor in self:
if not sponsor[fname]:
sponsor[fname] = sponsor.partner_id[fname]
@@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class SponsorType(models.Model):
_inherit = "event.sponsor.type"
display_ribbon_style = fields.Selection([
('no_ribbon', 'No Ribbon'),
('Gold', 'Gold'),
('Silver', 'Silver'),
('Bronze', 'Bronze')], string='Ribbon Style')
@@ -1,201 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.osv import expression
from odoo.tools.mail import is_html_empty
from odoo.modules.module import get_resource_path
class Track(models.Model):
_name = "event.track"
_inherit = ['event.track']
# status management
is_accepted = fields.Boolean('Is Accepted', related='stage_id.is_accepted', readonly=True)
# speaker
partner_biography = fields.Html(
string='Biography', compute='_compute_partner_biography',
readonly=False, store=True)
partner_function = fields.Char(
'Job Position', related='partner_id.function',
compute_sudo=True, readonly=True)
partner_company_name = fields.Char(
'Company Name', related='partner_id.parent_name',
compute_sudo=True, readonly=True)
image = fields.Image(
string="Speaker Photo", compute="_compute_speaker_image",
readonly=False, store=True,
max_width=256, max_height=256)
# frontend description
website_image = fields.Image(string="Website Image", max_width=1024, max_height=1024)
website_image_url = fields.Char(
string='Image URL', compute='_compute_website_image_url',
compute_sudo=True, store=False)
# wishlist / visitors management
event_track_visitor_ids = fields.One2many(
'event.track.visitor', 'track_id', string="Track Visitors",
groups="event.group_event_user")
is_reminder_on = fields.Boolean('Is Reminder On', compute='_compute_is_reminder_on')
wishlist_visitor_ids = fields.Many2many(
'website.visitor', string="Visitor Wishlist",
compute="_compute_wishlist_visitor_ids", compute_sudo=True,
search="_search_wishlist_visitor_ids",
groups="event.group_event_user")
wishlist_visitor_count = fields.Integer(
string="# Wishlisted",
compute="_compute_wishlist_visitor_ids", compute_sudo=True,
groups="event.group_event_user")
wishlisted_by_default = fields.Boolean(
string='Always Wishlisted',
help="""If set, the talk will be starred for each attendee registered to the event. The attendee won't be able to un-star this talk.""")
# SPEAKER
@api.depends('partner_id')
def _compute_partner_biography(self):
for track in self:
if not track.partner_biography:
track.partner_biography = track.partner_id.website_description
elif track.partner_id and is_html_empty(track.partner_biography) and \
not is_html_empty(track.partner_id.website_description):
track.partner_biography = track.partner_id.website_description
@api.depends('partner_id')
def _compute_speaker_image(self):
for track in self:
if not track.image:
track.image = track.partner_id.image_256
# FRONTEND DESCRIPTION
@api.depends('image', 'partner_id.image_256')
def _compute_website_image_url(self):
for track in self:
if track.website_image:
track.website_image_url = self.env['website'].image_url(track, 'website_image', size=1024)
else:
track.website_image_url = '/website_event_track/static/src/img/event_track_default_%d.jpeg' % (track.id % 2)
# WISHLIST / VISITOR MANAGEMENT
@api.depends('wishlisted_by_default', 'event_track_visitor_ids.visitor_id',
'event_track_visitor_ids.partner_id', 'event_track_visitor_ids.is_wishlisted',
'event_track_visitor_ids.is_blacklisted')
@api.depends_context('uid')
def _compute_is_reminder_on(self):
current_visitor = self.env['website.visitor']._get_visitor_from_request(force_create=False)
if self.env.user._is_public() and not current_visitor:
for track in self:
track.is_reminder_on = track.wishlisted_by_default
else:
if self.env.user._is_public():
domain = [('visitor_id', '=', current_visitor.id)]
elif 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)]
event_track_visitors = self.env['event.track.visitor'].sudo().search_read(
expression.AND([
domain,
[('track_id', 'in', self.ids)]
]), fields=['track_id', 'is_wishlisted', 'is_blacklisted']
)
wishlist_map = {
track_visitor['track_id'][0]: {
'is_wishlisted': track_visitor['is_wishlisted'],
'is_blacklisted': track_visitor['is_blacklisted']
} for track_visitor in event_track_visitors
}
for track in self:
if wishlist_map.get(track.id):
track.is_reminder_on = wishlist_map.get(track.id)['is_wishlisted'] or (track.wishlisted_by_default and not wishlist_map[track.id]['is_blacklisted'])
else:
track.is_reminder_on = track.wishlisted_by_default
@api.depends('event_track_visitor_ids.visitor_id', 'event_track_visitor_ids.is_wishlisted')
def _compute_wishlist_visitor_ids(self):
results = self.env['event.track.visitor'].read_group(
[('track_id', 'in', self.ids), ('is_wishlisted', '=', True)],
['track_id', 'visitor_id:array_agg'],
['track_id']
)
visitor_ids_map = {result['track_id'][0]: result['visitor_id'] for result in results}
for track in self:
track.wishlist_visitor_ids = visitor_ids_map.get(track.id, [])
track.wishlist_visitor_count = len(visitor_ids_map.get(track.id, []))
def _search_wishlist_visitor_ids(self, operator, operand):
if operator == "not in":
raise NotImplementedError("Unsupported 'Not In' operation on track wishlist visitors")
track_visitors = self.env['event.track.visitor'].sudo().search([
('visitor_id', operator, operand),
('is_wishlisted', '=', True)
])
return [('id', 'in', track_visitors.track_id.ids)]
# ------------------------------------------------------------
# CRUD
# -----------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
tracks = super(Track, self).create(vals_list)
for track in tracks:
track._synchronize_with_stage(track.stage_id)
return tracks
def write(self, vals):
if vals.get('stage_id'):
stage = self.env['event.track.stage'].browse(vals['stage_id'])
self._synchronize_with_stage(stage)
return super(Track, self).write(vals)
def _synchronize_with_stage(self, stage):
if stage.is_done:
self.is_published = True
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def _get_event_track_visitors(self, force_create=False):
self.ensure_one()
force_visitor_create = self.env.user._is_public()
visitor_sudo = self.env['website.visitor']._get_visitor_from_request(force_create=force_visitor_create)
if visitor_sudo:
visitor_sudo._update_visitor_last_visit()
if self.env.user._is_public():
domain = [('visitor_id', '=', visitor_sudo.id)]
elif visitor_sudo:
domain = [
'|',
('partner_id', '=', self.env.user.partner_id.id),
('visitor_id', '=', visitor_sudo.id)
]
else:
domain = [('partner_id', '=', self.env.user.partner_id.id)]
track_visitors = self.env['event.track.visitor'].sudo().search(
expression.AND([domain, [('track_id', 'in', self.ids)]])
)
missing = self - track_visitors.track_id
if missing and force_create:
track_visitors += self.env['event.track.visitor'].sudo().create([{
'visitor_id': visitor_sudo.id,
'partner_id': self.env.user.partner_id.id if not self.env.user._is_public() else False,
'track_id': track.id,
} for track in missing])
return track_visitors
@@ -1,13 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class TrackStage(models.Model):
_inherit = 'event.track.stage'
is_accepted = fields.Boolean(
string='Accepted Stage',
help='Accepted tracks are displayed in agenda views but not accessible.')
is_done = fields.Boolean(help='Done tracks are automatically published so that they are available in frontend.')
@@ -1,5 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
event_track_visitor_access_all,event.track.visitor.access.all,model_event_track_visitor,,0,0,0,0
event_track_visitor_access_manager,event.track.visitor.access.manager,model_event_track_visitor,event.group_event_manager,1,1,1,1
event_track_tag_category_access_public,event.track.tag.category.access,model_event_track_tag_category,,1,0,0,0
event_track_tag_category_access_manager,event.track.tag.category.access.manager,model_event_track_tag_category,event.group_event_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 event_track_visitor_access_all event.track.visitor.access.all model_event_track_visitor 0 0 0 0
3 event_track_visitor_access_manager event.track.visitor.access.manager model_event_track_visitor event.group_event_manager 1 1 1 1
4 event_track_tag_category_access_public event.track.tag.category.access model_event_track_tag_category 1 0 0 0
5 event_track_tag_category_access_manager event.track.tag.category.access.manager model_event_track_tag_category event.group_event_manager 1 1 1 1
@@ -1,13 +0,0 @@
odoo.define('website_event_track_online.set_customize_options', function (require) {
"use strict";
var EventSpecificOptions = require('website_event.set_customize_options').EventSpecificOptions;
EventSpecificOptions.include({
xmlDependencies: (EventSpecificOptions.prototype.xmlDependencies || [])
.concat([
'/website_event_track_online/static/src/xml/website_event_customize_options.xml',
]),
});
});
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="website_event_track.customize_options" t-extend="website_event.customize_options">
<t t-jquery="a[name='display-showcase-talks']" t-operation="replace">
</t>
<t t-jquery="a[name='allow-talk-proposal']" t-operation="replace">
</t>
</t>
</templates>
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Event Track Online Assets Frontend">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/website_event_track_online/static/src/scss/event_track_templates.scss"/>
<link rel="stylesheet" type="text/scss" href="/website_event_track_online/static/src/scss/pwa_frontend.scss"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/website_event_track_online/static/src/js/event_track_reminder.js"></script>
<script type="text/javascript" src="/website_event_track_online/static/src/js/website_event_customize_options.js"></script>
<script type="text/javascript" src="/website_event_track_online/static/lib/idb-keyval/idb-keyval.js"/>
<script type="text/javascript" src="/website_event_track_online/static/src/js/website_event_pwa_widget.js"/>
</xpath>
</template>
</odoo>
@@ -1,20 +0,0 @@
<?xml version="1.0"?>
<odoo><data>
<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.event_event_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='website_menu']" position="after">
<label for="website_track" string="Showcase Tracks"/>
<field name="website_track"/>
<label for="website_track_proposal" string="Allow Track Proposals"/>
<field name="website_track_proposal"/>
</xpath>
</field>
</record>
</data></odoo>
@@ -1,18 +0,0 @@
<?xml version="1.0"?>
<odoo><data>
<menuitem id="event_track_tag_category_menu"
name="Track Tag Categories"
action="event_track_tag_category_action"
parent="event.menu_event_configuration"
groups="base.group_no_one"
sequence="33"/>
<menuitem id="event_track_visitor_menu"
name="Track Visitors"
sequence="38"
action="event_track_visitor_action"
parent="event.menu_event_configuration"
groups="base.group_no_one"/>
</data></odoo>
@@ -1,24 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record id="event_sponsor_type_view_tree" model="ir.ui.view">
<field name="name">event.sponsor.type.view.tree.inherit.online</field>
<field name="model">event.sponsor.type</field>
<field name="inherit_id" ref="website_event_track.event_sponsor_type_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="display_ribbon_style"/>
</xpath>
</field>
</record>
<record id="event_sponsor_type_view_form" model="ir.ui.view">
<field name="name">event.sponsor.type.view.form.inherit.online</field>
<field name="model">event.sponsor.type</field>
<field name="inherit_id" ref="website_event_track.event_sponsor_type_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="display_ribbon_style"/>
</xpath>
</field>
</record>
</odoo>
@@ -1,68 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record id="event_sponsor_view_search" model="ir.ui.view">
<field name="name">event.sponsor.view.search.inherit.online</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="name"/>
<field name="email"/>
<field name="phone"/>
</xpath>
</field>
</record>
<record id="event_sponsor_view_tree" model="ir.ui.view">
<field name="name">event.sponsor.view.tree.inherit.online</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="multi_edit">1</attribute>
</xpath>
<xpath expr="//field[@name='partner_id']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//field[@name='partner_id']" position="after">
<field name="name"/>
<field name="email"/>
<field name="phone"/>
<field name="mobile"/>
</xpath>
</field>
</record>
<record id="event_sponsor_view_form" model="ir.ui.view">
<field name="name">event.sponsor.view.form.inherit.online</field>
<field name="model">event.sponsor</field>
<field name="inherit_id" ref="website_event_track.event_sponsor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='image_128']" position="replace">
<field name="image_512" widget="image" class="oe_avatar"/>
</xpath>
<xpath expr="//h1//field[@name='partner_id']" position="replace">
<div class="oe_title">
<label for="name" class="oe_edit_only" string="Sponsor Name"/>
<h1><field name="name" placeholder="e.g. : OpenWood Decoration"/></h1>
</div>
</xpath>
<xpath expr="//field[@name='event_id']" position="after">
<field name="partner_id" string="Partner"/>
</xpath>
<xpath expr="//field[@name='partner_email']" position="replace">
<field name="email" widget="email" string="Email"
placeholder="e.g. : open.wood.decoration@example.com"/>
</xpath>
<xpath expr="//field[@name='url']" position="attributes">
<attribute name="placeholder">e.g. : https://www.odoo.com</attribute>
</xpath>
<xpath expr="//field[@name='partner_phone']" position="replace">
<field name="phone" widget="phone" string="Phone" options="{'enable_sms': True}"/>
</xpath>
<xpath expr="//field[@name='partner_mobile']" position="replace">
<field name="mobile" widget="phone" string="Mobile" options="{'enable_sms': True}"/>
</xpath>
</field>
</record>
</odoo>
@@ -1,28 +0,0 @@
<?xml version="1.0"?>
<odoo><data>
<record id="event_track_stage_view_form" model="ir.ui.view">
<field name="name">event.track.stage.view.form.inherit.online</field>
<field name="model">event.track.stage</field>
<field name="inherit_id" ref="website_event_track.event_track_stage_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='is_done']" position="before">
<field name="is_accepted"/>
</xpath>
</field>
</record>
<record id="event_track_stage_view_tree" model="ir.ui.view">
<field name="name">event.track.stage.view.tree.inherit.online</field>
<field name="model">event.track.stage</field>
<field name="inherit_id" ref="website_event_track.event_track_stage_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="is_accepted"/>
<field name="is_done"/>
<field name="is_cancel"/>
</xpath>
</field>
</record>
</data></odoo>
@@ -1,69 +0,0 @@
<?xml version="1.0"?>
<odoo><data>
<record id="event_track_tag_category_view_form" model="ir.ui.view">
<field name="name">event.track.tag.category.view.form</field>
<field name="model">event.track.tag.category</field>
<field name="arch" type="xml">
<form string="Track Tag Category">
<sheet>
<group>
<field name="name"/>
<field name="tag_ids" context="{'default_category_id': active_id}">
<tree string="Tags" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="color" widget="color_picker"/>
</tree>
</field>
</group>
</sheet>
</form>
</field>
</record>
<record id="event_track_tag_category_view_list" model="ir.ui.view">
<field name="name">event.track.tag.category.view.list</field>
<field name="model">event.track.tag.category</field>
<field name="arch" type="xml">
<tree string="Track Tags Category">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</tree>
</field>
</record>
<record id="event_track_tag_category_action" model="ir.actions.act_window">
<field name="name">Track Tag Categories</field>
<field name="res_model">event.track.tag.category</field>
<field name="view_mode">tree,form</field>
</record>
<record id="event_track_tag_view_form" model="ir.ui.view">
<field name="name">event.track.tag.view.form.inherit.online</field>
<field name="model">event.track.tag</field>
<field name="inherit_id" ref="website_event_track.view_event_track_tag_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="color" widget="color_picker"/>
<field name="category_id"/>
</xpath>
</field>
</record>
<record id="event_track_tag_view_list" model="ir.ui.view">
<field name="name">event.track.tag.view.list.inherit.online</field>
<field name="model">event.track.tag</field>
<field name="inherit_id" ref="website_event_track.view_event_track_tag_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="before">
<field name="sequence" widget="handle"/>
</xpath>
<xpath expr="//field[@name='name']" position="after">
<field name="color" widget="color_picker"/>
<field name="category_id"/>
</xpath>
</field>
</record>
</data></odoo>
@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Set original template for tracks / track as tracking visitors -->
<record model="ir.ui.view" id="website_event_track.tracks">
<field name="track" eval="True"/>
</record>
<record model="ir.ui.view" id="website_event_track.track_view">
<field name="track" eval="True"/>
</record>
<!-- Add wishlist button on tracks / track templates -->
<template id="tracks_wishlist"
name="Wishlist on Tracks"
inherit_id="website_event_track.tracks"
customize_show="True">
<xpath expr="//h3" position="replace">
<h3 class="mt0 mb0 d-flex">
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }" class="text-secondary"><span t-field="track.name"> </span></a>
<small t-if="not track.website_published" class="badge badge-danger">unpublished</small>
<t t-call="website_event_track_online.track_widget_reminder"/>
</h3>
</xpath>
</template>
<template id="track_view_wishlist"
name="Wishlist on Track"
inherit_id="website_event_track.track_view"
customize_show="True">
<xpath expr="//h1" position="replace">
<div class="d-flex">
<h1 t-field="track.name"/>
<div class="ml-2 pt-1">
<t t-call="website_event_track_online.track_widget_reminder"/>
</div>
</div>
</xpath>
</template>
<!-- Pimp agenda based on accepted / published -->
<template id="agenda"
name="Track Online: Agenda"
inherit_id="website_event_track.agenda">
<xpath expr="//t[@name='track_agenda_track_cell']" position="replace">
<t t-if="track" name="track_agenda_track_cell">
<t t-if="user_event_manager or track.is_published">
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }">
<span t-esc="track.name"/>
</a>
</t>
<t t-else="">
<span t-esc="track.name"/>
</t>
<div class="d-flex text-muted">
<small t-esc="track.partner_name or track.partner_id.name"/>
<span t-if="user_event_manager and not track.website_published" class=" ml-auto badge badge-danger">unpublished</span>
</div>
</t>
</xpath>
</template>
<!-- Pimp sponsor view -->
<template id="event_sponsor"
name="Track Online: Sponsors"
inherit_id="website_event_track.event_sponsor">
<xpath expr="//div[@t-if='event.sponsor_ids']" position="replace">
<div class="container mt32 mb16 d-print-none" t-if="event.sponsor_ids">
<div t-attf-class="d-flex flex-wrap mb-5 #{'' if (len(event.sponsor_ids) > 10) else 'justify-content-md-center'}">
<t t-foreach="event.sponsor_ids" t-as="sponsor">
<t t-if="sponsor.url">
<a class="o_wevent_sponsor_card" target="_blank" t-att-href="sponsor.url">
<div class="h-100 shadow-sm p-2">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "img img-fluid"}'/>
<div t-if="sponsor.sponsor_type_id.display_ribbon_style and sponsor.sponsor_type_id.display_ribbon_style != 'no_ribbon'" class="ribbon-wrapper">
<div t-field="sponsor.sponsor_type_id" t-attf-class="ribbon ribbon_#{sponsor.sponsor_type_id.display_ribbon_style}"/>
</div>
</div>
</a>
</t>
<t t-if="not sponsor.url">
<div class="o_wevent_sponsor_card">
<div class="h-100 shadow-sm p-2">
<span t-field="sponsor.image_128"
t-options='{"widget": "image", "class": "img img-fluid"}'/>
<div t-if="sponsor.sponsor_type_id.display_ribbon_style and sponsor.sponsor_type_id.display_ribbon_style != 'no_ribbon'" class="ribbon-wrapper">
<div t-field="sponsor.sponsor_type_id" t-attf-class="ribbon ribbon_#{sponsor.sponsor_type_id.display_ribbon_style}"/>
</div>
</div>
</div>
</t>
</t>
</div>
</div>
</xpath>
</template>
</odoo>
@@ -1,195 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Revamped agenda : Will need to replace agenda_online from website_event_track in master -->
<template id="agenda_online" name="Track Online: Agenda">
<t t-call="website_event.layout">
<div class="o_wevent_online o_weagenda_index">
<!-- Options -->
<t t-set="option_track_wishlist" t-value="not event.is_done and request.website.viewref('website_event_track_online.agenda_topbar_wishlist').active"/>
<!-- Topbar -->
<t t-call="website_event_track_online.agenda_topbar"/>
<!-- Drag/Drop Area -->
<div class="oe_structure" id="oe_structure_website_event_track_agenda_1"/>
<!-- Content -->
<div class="container">
<div class="row mb-5">
<t t-call="website_event_track_online.agenda_main"/>
</div>
</div>
<!-- Drag/Drop Area -->
<div class="oe_structure" id="oe_structure_website_event_track_agenda_2"/>
</div>
</t>
</template>
<!-- ============================================================ -->
<!-- TOPBAR: BASE NAVIGATION -->
<!-- ============================================================ -->
<!-- Main topbar -->
<template id="agenda_topbar" name="Agenda Tools">
<nav class="navbar navbar-light border-top shadow-sm d-print-none">
<div class="container">
<div class="d-flex flex-column flex-sm-row justify-content-between w-100">
<ul class="o_weagenda_topbar_filters o_wevent_index_topbar_filters nav">
</ul>
<div class="form-inline pl-sm-3 pr-0">
<label class="invisible text-muted mr-2" id="search_summary"><span id="search_number" class="mr-1">0</span>Results</label>
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
</div>
</div>
</div>
</nav>
</template>
<!-- Option: Tracks display: optional wishlist -->
<template id="agenda_topbar_wishlist"
inherit_id="website_event_track_online.agenda_topbar"
name="Allow Wishlists"
active="True"
customize_show="True">
<xpath expr="//ul[hasclass('o_weagenda_topbar_filters')]" position="inside">
</xpath>
</template>
<!-- ============================================================ -->
<!-- CONTENT: MAIN TEMPLATES -->
<!-- ============================================================ -->
<!-- Agenda Main Display -->
<template id="agenda_main" name="Tracks: Main Display">
<!-- No tracks -->
<div class="col-12" t-if="not tracks_by_days">
<div class="h2 mb-3">No track found.</div>
<div t-if="search_key" class="alert alert-info text-center">
<p class="m-0">We did not find any track matching your <strong t-esc="search_key"/> search.</p>
</div>
<div t-else="" class="alert alert-info text-center" groups="event.group_event_manager">
<p class="m-0">Schedule some tracks to get started !</p>
</div>
</div>
<section t-else="" class="col-12" t-foreach="days" t-as="day">
<!-- DAY HEADER -->
<div class="o_we_track_day_header mt-3 w-100 d-flex justify-content-between align-items-center">
<div class="d-flex">
<span class="h1 m-0 font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'EEEE dd'}"/>
<div class="d-flex flex-column ml-2">
<span class="font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'MMMM'}"/>
<span class="font-weight-bold" t-esc="day"
t-options="{'widget': 'date', 'format': 'YYYY'}"/>
</div>
</div>
<small class="float-right text-muted align-self-end"><t t-esc="tracks_by_days[day]"/> tracks</small>
</div>
<hr class="mt-2 pb-1 mb-1"/>
<!-- Day Agenda -->
<div class="o_we_online_agenda">
<table id="table_search" class="table table-sm border-0">
<!--Header-->
<tr>
<th class="border-0 bg-white position-sticky"/>
<t t-foreach="locations" t-as="location">
<th t-if="location" class="active text-center">
<span t-esc="location and location.name or 'Unknown'"/>
</th>
</t>
</tr>
<!-- Time Slots -->
<t t-set="used_cells" t-value="[]"/>
<t t-foreach="time_slots[day]" t-as="time_slot">
<t t-set="is_round_hour" t-value="time_slot == time_slot.replace(minute=0)"/>
<t t-set="is_half_hour" t-value="time_slot == time_slot.replace(minute=30)"/>
<tr t-att-class="'%s' % ('active' if is_round_hour else '')">
<td class="active">
<b t-if="is_round_hour" t-esc="time_slots[day][time_slot]['formatted_time']"/>
</td>
<t t-foreach="locations" t-as="location">
<t t-set="tracks" t-value="time_slots[day][time_slot].get(location, {})"/>
<t t-if="tracks">
<t t-foreach="tracks" t-as="track">
<t t-if="track.location_id and track.location_id == location">
<td t-att-rowspan="tracks[track]['rowspan']"
t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''} o_location_size_#{len(locations)}">
<t t-call="website_event_track_online.agenda_main_track"/>
</td>
</t>
<t t-else="">
<td t-att-colspan="len(locations)-1"
t-att-rowspan="tracks[track]['rowspan']"
t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''} o_location_size_#{len(locations)}">
<t t-call="website_event_track_online.agenda_main_track"/>
</td>
</t>
<t t-set="used_cells" t-value="used_cells + tracks[track]['occupied_cells']"/>
</t>
</t>
<t t-elif="location and (time_slot, location) not in used_cells">
<td t-att-rowspan="1"
t-att-class="'o_location_size_%s %s' % (len(locations),
'o_we_agenda_time_slot_half' if is_half_hour else
'o_we_agenda_time_slot_main' if is_round_hour else
''
)"><div/></td>
</t>
</t>
</tr>
</t>
</table>
</div>
</section>
</template>
<template id="agenda_main_track" name="Track Agenda: Track">
<div class="d-flex flex-column h-100">
<div class="d-flex justify-content-end flex-wrap-reverse o_weagenda_track_badges">
<small t-if="not track.website_published and user_event_manager and track.stage_id.is_accepted"
title="Unpublished"
class="ml-1 badge badge-danger o_wevent_online_badge_unpublished">Unpublished</small>
<small t-if="not track.stage_id.is_accepted" title="Not Accepted"
class="ml-1 badge badge-danger o_wevent_online_badge_unpublished">Not Accepted</small>
<span t-if="option_track_wishlist">
<t t-call="website_event_track_online.track_widget_reminder">
<t t-set="reminder_light" t-value="True"/>
<t t-set="reminder_small" t-value="True"/>
<t t-set="light_theme" t-value="False"/>
</t>
</span>
</div>
<div class="o_we_agenda_card_content d-flex flex-column justify-content-center my-1">
<div t-att-class="'text-black' if track.website_published or user_event_manager else 'text-muted'"
t-att-onclick="'window.location=\'/event/%s/track/%s\'' % (slug(event), slug(track))
if track.website_published or user_event_manager else ''">
<span class="o_we_agenda_card_title text-bold" t-esc="track.name"/>
</div>
<div class="text-muted text-center">
<small t-if="track.partner_id or track.partner_name">
<t t-esc="track.partner_id.sudo().name or track.partner_name"/></small>
</div>
<div class="d-flex justify-content-center flex-wrap">
<t t-foreach="track.tag_ids" t-as="tag">
<span t-if="tag.color" t-att-title="tag.name"
t-attf-class="mr-1 mt-1 badge #{'o_tag_color_'+str(tag.color)}" t-esc="tag.name"
t-attf-onclick="
var value = '#{tag.name}' ;
var target = $('#event_track_search');
if (target.val() == value) { target.val(''); } else { target.val(value); }
target.trigger('input');
"
/>
</t>
</div>
</div>
</div>
</template>
</odoo>
@@ -1,75 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record id="website_visitor_action_from_track" model="ir.actions.act_window">
<field name="name">Visitors Wishlist</field>
<field name="res_model">website.visitor</field>
<field name="view_mode">kanban,tree,form,graph</field>
<field name="domain">[('event_track_wishlisted_ids', 'in', [active_id])]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Wait for visitors to wishlist your tracks
</p>
</field>
</record>
<record id="event_track_view_search" model="ir.ui.view">
<field name="name">event.track.view.search.inherit.event.track.online</field>
<field name="model">event.track</field>
<field name="inherit_id" ref="website_event_track.view_event_track_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='message_needaction']" position="after">
<filter string="Always Wishlisted" name="filter_wishlisted_by_default" domain="[('wishlisted_by_default', '=', True)]"/>
</xpath>
</field>
</record>
<record id="event_track_view_tree" model="ir.ui.view" >
<field name="name">event.track.view.tree.inherit.event.track.online</field>
<field name="model">event.track</field>
<field name="inherit_id" ref="website_event_track.view_event_track_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='event_id']" position="after">
<field name="wishlisted_by_default" optional="hide"/>
<field name="wishlist_visitor_count" optional="hide"/>
</xpath>
</field>
</record>
<record id="event_track_view_form" model="ir.ui.view" >
<field name="name">event.track.view.form.inherit.event.track.online</field>
<field name="model">event.track</field>
<field name="inherit_id" ref="website_event_track.view_event_track_form"/>
<field name="arch" type="xml">
<field name="active" position='after'>
<field name="wishlisted_by_default"/>
</field>
<field name="is_published" position="before">
<button name="%(website_event_track_online.website_visitor_action_from_track)d"
type="action"
class="oe_stat_button"
icon="fa-bell">
<field name="wishlist_visitor_count" string="Wishlisted By" widget="statinfo"/>
</button>
</field>
<xpath expr="//field[@name='image']" position="replace">
<field name="website_image" widget="image" class="oe_avatar"/>
</xpath>
<xpath expr="//field[@name='partner_biography']" position="after">
<field name="image" widget="image" class="oe_avatar float-left"/>
</xpath>
</field>
</record>
<record id="event_track_action_from_visitor" model="ir.actions.act_window">
<field name="name">Wishlisted Tracks</field>
<field name="res_model">event.track</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('wishlist_visitor_ids', 'in', [active_id])]</field>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
No track wishlisted by this visitor
</p>
</field>
</record>
</odoo>
@@ -4,7 +4,7 @@
<record id="event_track_visitor_view_search" model="ir.ui.view" >
<field name="name">event.track.visitor.view.search.inherit.quiz</field>
<field name="model">event.track.visitor</field>
<field name="inherit_id" ref="website_event_track_online.event_track_visitor_view_search"/>
<field name="inherit_id" ref="website_event_track.event_track_visitor_view_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='is_wishlisted']" position="after">
<field name="quiz_completed"/>
@@ -15,7 +15,7 @@
<record id="event_track_visitor_view_form" model="ir.ui.view">
<field name="name">event.track.visitor.view.form.inherit.quiz</field>
<field name="model">event.track.visitor</field>
<field name="inherit_id" ref="website_event_track_online.event_track_visitor_view_form"/>
<field name="inherit_id" ref="website_event_track.event_track_visitor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='is_wishlisted']" position="after">
<field name="quiz_completed"/>
@@ -27,7 +27,7 @@
<record id="event_track_visitor_view_list" model="ir.ui.view">
<field name="name">event.track.visitor.view.list.inherit.quiz</field>
<field name="model">event.track.visitor</field>
<field name="inherit_id" ref="website_event_track_online.event_track_visitor_view_list"/>
<field name="inherit_id" ref="website_event_track.event_track_visitor_view_list"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='is_wishlisted']" position="after">
<field name="quiz_completed"/>
@@ -11,7 +11,7 @@
'website': 'https://www.odoo.com/page/events',
'description': "",
'depends': [
'website_event_track_online',
'website_event_track',
],
'data': [
'views/assets.xml',
@@ -3,16 +3,16 @@
from ast import literal_eval
from pytz import timezone, utc
from werkzeug.exceptions import Forbidden, NotFound
from werkzeug.exceptions import NotFound
from odoo import fields, http, _
from odoo.addons.website_event_track_online.controllers.event_track import EventTrackOnlineController
from odoo.addons.website_event_track.controllers.event_track import EventTrackController
from odoo.http import request
from odoo.osv import expression
from odoo.tools import is_html_empty
class WebsiteEventSessionController(EventTrackOnlineController):
class WebsiteEventSessionController(EventTrackController):
def _get_event_tracks_base_domain(self, event):
""" Base domain for displaying tracks. Restrict to accepted or published
@@ -6,7 +6,7 @@ from unittest.mock import patch
from odoo import fields
from odoo.addons.website.models.website_visitor import WebsiteVisitor
from odoo.addons.website_event_track_online.tests.common import TestEventTrackOnlineCommon
from odoo.addons.website_event_track.tests.common import TestEventTrackOnlineCommon
class TestTrackSuggestions(TestEventTrackOnlineCommon):
@@ -2,7 +2,7 @@
<odoo>
<template id="agenda_main_track" name="Track Agenda: Track (Session)"
inherit_id="website_event_track_online.agenda_main_track">
inherit_id="website_event_track.agenda_main_track">
<xpath expr="//div[hasclass('o_weagenda_track_badges')]/small[1]" position="before">
<small t-if="track.is_track_live and not track.is_track_done and track.website_published"
class="mx-1 badge badge-danger">Live
@@ -228,7 +228,7 @@
t-esc="track.date"
t-options="{'widget': 'datetime', 'time_only': True, 'format': 'short'}"/>
<t t-if="option_track_wishlist"
t-call="website_event_track_online.track_widget_reminder">
t-call="website_event_track.track_widget_reminder">
<t t-set="reminder_small" t-value="True"/>
</t>
</t>
@@ -268,7 +268,7 @@
<div t-if="track.website_image_url" class="card-img-top"
t-attf-style="padding-top: 50%; background-image: url(#{track.website_image_url}); background-size: cover; background-position:center">
<span t-if="option_track_wishlist and not track.is_track_live" class="position-absolute h3 mt-2 mr-2" style="right: 0; top: 0;">
<t t-call="website_event_track_online.track_widget_reminder">
<t t-call="website_event_track.track_widget_reminder">
<t t-set="reminder_light" t-value="True"/>
<t t-set="light_theme" t-value="True"/>
</t>
@@ -277,7 +277,7 @@
<div t-else="" class="o_wesession_gradient card-img-top position-relative"
style="padding-top: 50%;">
<span t-if="option_track_wishlist" class="position-absolute h3 mt-2 mr-2" style="right: 0; top: 0;">
<t t-call="website_event_track_online.track_widget_reminder">
<t t-call="website_event_track.track_widget_reminder">
<t t-set="reminder_light" t-value="True"/>
</t>
</span>
@@ -6,11 +6,11 @@
<field name="active" eval="False"/>
<field name="customize_show" eval="False"/>
</record>
<record id="website_event_track_online.tracks_wishlist" model="ir.ui.view">
<record id="website_event_track.tracks_wishlist" model="ir.ui.view">
<field name="active" eval="False"/>
<field name="customize_show" eval="False"/>
</record>
<record id="website_event_track_online.track_view_wishlist" model="ir.ui.view">
<record id="website_event_track.track_view_wishlist" model="ir.ui.view">
<field name="active" eval="False"/>
<field name="customize_show" eval="False"/>
</record>
@@ -68,7 +68,7 @@
<div class="o_we_track_action_buttons d-flex justify-content-md-end align-items-center flex-wrap">
<div class="o_we_track_reminder_button my-1">
<div t-if="option_track_wishlist and not track.is_track_done"
t-call="website_event_track_online.track_widget_reminder">
t-call="website_event_track.track_widget_reminder">
<t t-set="reminder_small" t-value="False"/>
<t t-set="reminder_light" t-value="False"/>
</div>