diff --git a/addons/im_livechat/static/src/legacy/public_livechat.js b/addons/im_livechat/static/src/legacy/public_livechat.js
index 5a29d6e667f..4f14fbb06f7 100644
--- a/addons/im_livechat/static/src/legacy/public_livechat.js
+++ b/addons/im_livechat/static/src/legacy/public_livechat.js
@@ -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');
diff --git a/addons/test_event_full/__manifest__.py b/addons/test_event_full/__manifest__.py
index 363252c06a6..bd0263e341c 100644
--- a/addons/test_event_full/__manifest__.py
+++ b/addons/test_event_full/__manifest__.py
@@ -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',
diff --git a/addons/website_event/tests/common.py b/addons/website_event/tests/common.py
index 8b447777d39..1dbf91c8e49 100644
--- a/addons/website_event/tests/common.py
+++ b/addons/website_event/tests/common.py
@@ -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)
diff --git a/addons/website_event_track/__manifest__.py b/addons/website_event_track/__manifest__.py
index bdcf7efc16c..74618521e33 100644
--- a/addons/website_event_track/__manifest__.py
+++ b/addons/website_event_track/__manifest__.py
@@ -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',
],
}
diff --git a/addons/website_event_track/controllers/__init__.py b/addons/website_event_track/controllers/__init__.py
index 5d4b25db9c0..8779baf6109 100644
--- a/addons/website_event_track/controllers/__init__.py
+++ b/addons/website_event_track/controllers/__init__.py
@@ -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
diff --git a/addons/website_event_track_online/controllers/event_track.py b/addons/website_event_track/controllers/event_track.py
similarity index 69%
rename from addons/website_event_track_online/controllers/event_track.py
rename to addons/website_event_track/controllers/event_track.py
index 8df8b5deb5c..e338f384580 100644
--- a/addons/website_event_track_online/controllers/event_track.py
+++ b/addons/website_event_track/controllers/event_track.py
@@ -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//track''',
+ '''/event//track/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//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//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//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//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//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
diff --git a/addons/website_event_track/controllers/main.py b/addons/website_event_track/controllers/main.py
deleted file mode 100644
index 13477d422b4..00000000000
--- a/addons/website_event_track/controllers/main.py
+++ /dev/null
@@ -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//track''',
- '''/event//track/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//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//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//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//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})
diff --git a/addons/website_event_track/controllers/webmanifest.py b/addons/website_event_track/controllers/webmanifest.py
new file mode 100644
index 00000000000..a777ff10303
--- /dev/null
+++ b/addons/website_event_track/controllers/webmanifest.py
@@ -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
diff --git a/addons/website_event_track/data/event_track_data.xml b/addons/website_event_track/data/event_track_data.xml
index 707cb8255d2..33cf0fb5fad 100644
--- a/addons/website_event_track/data/event_track_data.xml
+++ b/addons/website_event_track/data/event_track_data.xml
@@ -33,14 +33,17 @@
@@ -58,11 +61,13 @@
Announced
3
3
+
Published
4
4
+
diff --git a/addons/website_event_track/data/event_track_demo.xml b/addons/website_event_track/data/event_track_demo.xml
index 62e6f0a7af5..4eeead8b65f 100644
--- a/addons/website_event_track/data/event_track_demo.xml
+++ b/addons/website_event_track/data/event_track_demo.xml
@@ -333,6 +333,7 @@
1
+
1
+
2
+
@@ -572,6 +575,7 @@
7
+
diff --git a/addons/website_event_track/data/event_track_tag_demo.xml b/addons/website_event_track/data/event_track_tag_demo.xml
index 1d4b4234e3e..bffa1d6cd05 100644
--- a/addons/website_event_track/data/event_track_tag_demo.xml
+++ b/addons/website_event_track/data/event_track_tag_demo.xml
@@ -1,28 +1,49 @@
+
+ Audience
+ 10
+
+
+ Format
+ 20
+
+
Consumers
1
+
+ 1
Sales
2
+
+ 2
Research
3
+
+ 3
Lightning Talks
4
+
+ 11
Round Table
5
+
+ 12
Keynote
6
+
+ 13
diff --git a/addons/website_event_track_online/data/event_track_visitor_demo.xml b/addons/website_event_track/data/event_track_visitor_demo.xml
similarity index 100%
rename from addons/website_event_track_online/data/event_track_visitor_demo.xml
rename to addons/website_event_track/data/event_track_visitor_demo.xml
diff --git a/addons/website_event_track/models/__init__.py b/addons/website_event_track/models/__init__.py
index eb261d9cca2..a9c5599582c 100644
--- a/addons/website_event_track/models/__init__.py
+++ b/addons/website_event_track/models/__init__.py
@@ -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
diff --git a/addons/website_event_track/models/event_sponsor.py b/addons/website_event_track/models/event_sponsor.py
index 5966d183dec..0fca8a53a07 100644
--- a/addons/website_event_track/models/event_sponsor.py
+++ b/addons/website_event_track/models/event_sponsor.py
@@ -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:
diff --git a/addons/website_event_track/models/event_sponsor_type.py b/addons/website_event_track/models/event_sponsor_type.py
index 3d3cd5749c8..8eb3eb71fbd 100644
--- a/addons/website_event_track/models/event_sponsor_type.py
+++ b/addons/website_event_track/models/event_sponsor_type.py
@@ -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')
diff --git a/addons/website_event_track/models/event_track.py b/addons/website_event_track/models/event_track.py
index 04d2d9e6514..2ec57bddde4 100644
--- a/addons/website_event_track/models/event_track.py
+++ b/addons/website_event_track/models/event_track.py
@@ -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
diff --git a/addons/website_event_track/models/event_track_stage.py b/addons/website_event_track/models/event_track_stage.py
index 022237db4f5..9e1a177c0f3 100644
--- a/addons/website_event_track/models/event_track_stage.py
+++ b/addons/website_event_track/models/event_track_stage.py
@@ -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')
diff --git a/addons/website_event_track/models/event_track_tag.py b/addons/website_event_track/models/event_track_tag.py
index 997a2c72f46..6efcba1e138 100644
--- a/addons/website_event_track/models/event_track_tag.py
+++ b/addons/website_event_track/models/event_track_tag.py
@@ -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 !"),
diff --git a/addons/website_event_track_online/models/event_track_tag.py b/addons/website_event_track/models/event_track_tag_category.py
similarity index 64%
rename from addons/website_event_track_online/models/event_track_tag.py
rename to addons/website_event_track/models/event_track_tag_category.py
index b04fb7bf251..05f14e7abed 100644
--- a/addons/website_event_track_online/models/event_track_tag.py
+++ b/addons/website_event_track/models/event_track_tag_category.py
@@ -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")
diff --git a/addons/website_event_track_online/models/event_track_visitor.py b/addons/website_event_track/models/event_track_visitor.py
similarity index 100%
rename from addons/website_event_track_online/models/event_track_visitor.py
rename to addons/website_event_track/models/event_track_visitor.py
diff --git a/addons/website_event_track_online/models/website.py b/addons/website_event_track/models/website.py
similarity index 100%
rename from addons/website_event_track_online/models/website.py
rename to addons/website_event_track/models/website.py
diff --git a/addons/website_event_track_online/models/website_visitor.py b/addons/website_event_track/models/website_visitor.py
similarity index 100%
rename from addons/website_event_track_online/models/website_visitor.py
rename to addons/website_event_track/models/website_visitor.py
diff --git a/addons/website_event_track/security/ir.model.access.csv b/addons/website_event_track/security/ir.model.access.csv
index 9a52bad509d..0cc16c6a31b 100644
--- a/addons/website_event_track/security/ir.model.access.csv
+++ b/addons/website_event_track/security/ir.model.access.csv
@@ -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
\ No newline at end of file
diff --git a/addons/website_event_track_online/static/lib/idb-keyval/idb-keyval.js b/addons/website_event_track/static/lib/idb-keyval/idb-keyval.js
similarity index 100%
rename from addons/website_event_track_online/static/lib/idb-keyval/idb-keyval.js
rename to addons/website_event_track/static/lib/idb-keyval/idb-keyval.js
diff --git a/addons/website_event_track_online/static/src/js/event_track_reminder.js b/addons/website_event_track/static/src/js/event_track_reminder.js
similarity index 100%
rename from addons/website_event_track_online/static/src/js/event_track_reminder.js
rename to addons/website_event_track/static/src/js/event_track_reminder.js
diff --git a/addons/website_event_track_online/static/src/js/service_worker.js b/addons/website_event_track/static/src/js/service_worker.js
similarity index 98%
rename from addons/website_event_track_online/static/src/js/service_worker.js
rename to addons/website_event_track/static/src/js/service_worker.js
index ac1a8249a15..a994c2123c4 100644
--- a/addons/website_event_track_online/static/src/js/service_worker.js
+++ b/addons/website_event_track/static/src/js/service_worker.js
@@ -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"];
diff --git a/addons/website_event_track_online/static/src/js/website_event_pwa_widget.js b/addons/website_event_track/static/src/js/website_event_pwa_widget.js
similarity index 97%
rename from addons/website_event_track_online/static/src/js/website_event_pwa_widget.js
rename to addons/website_event_track/static/src/js/website_event_pwa_widget.js
index 77d12e4bb1b..00c2a8ede33 100644
--- a/addons/website_event_track_online/static/src/js/website_event_pwa_widget.js
+++ b/addons/website_event_track/static/src/js/website_event_pwa_widget.js
@@ -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",
diff --git a/addons/website_event_track/static/src/js/website_event_track_set_customize_options.js b/addons/website_event_track/static/src/js/website_event_track_set_customize_options.js
deleted file mode 100644
index c193b16b0c6..00000000000
--- a/addons/website_event_track/static/src/js/website_event_track_set_customize_options.js
+++ /dev/null
@@ -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();
- });
- }
-
-});
-
-});
diff --git a/addons/website_event_track_online/static/src/scss/event_track_templates.scss b/addons/website_event_track/static/src/scss/event_track_templates.scss
similarity index 100%
rename from addons/website_event_track_online/static/src/scss/event_track_templates.scss
rename to addons/website_event_track/static/src/scss/event_track_templates.scss
diff --git a/addons/website_event_track_online/static/src/scss/pwa_frontend.scss b/addons/website_event_track/static/src/scss/pwa_frontend.scss
similarity index 100%
rename from addons/website_event_track_online/static/src/scss/pwa_frontend.scss
rename to addons/website_event_track/static/src/scss/pwa_frontend.scss
diff --git a/addons/website_event_track/static/src/xml/customize_options.xml b/addons/website_event_track/static/src/xml/customize_options.xml
deleted file mode 100644
index b7d64bca0db..00000000000
--- a/addons/website_event_track/static/src/xml/customize_options.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/website_event_track_online/static/src/xml/website_event_pwa.xml b/addons/website_event_track/static/src/xml/website_event_pwa.xml
similarity index 100%
rename from addons/website_event_track_online/static/src/xml/website_event_pwa.xml
rename to addons/website_event_track/static/src/xml/website_event_pwa.xml
diff --git a/addons/website_event_track/tests/__init__.py b/addons/website_event_track/tests/__init__.py
index 3773e715842..12b9da864c8 100644
--- a/addons/website_event_track/tests/__init__.py
+++ b/addons/website_event_track/tests/__init__.py
@@ -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
diff --git a/addons/website_event_track_online/tests/common.py b/addons/website_event_track/tests/common.py
similarity index 100%
rename from addons/website_event_track_online/tests/common.py
rename to addons/website_event_track/tests/common.py
diff --git a/addons/website_event_track/views/assets.xml b/addons/website_event_track/views/assets.xml
index d580f4e0106..120f828839d 100644
--- a/addons/website_event_track/views/assets.xml
+++ b/addons/website_event_track/views/assets.xml
@@ -2,10 +2,16 @@
-
+
+
+
+
+
-
+
+
+
diff --git a/addons/website_event_track/views/event_event_views.xml b/addons/website_event_track/views/event_event_views.xml
index 236f47a910b..a06ca4581ad 100644
--- a/addons/website_event_track/views/event_event_views.xml
+++ b/addons/website_event_track/views/event_event_views.xml
@@ -22,6 +22,12 @@
+
+
+
+
+
+
diff --git a/addons/website_event_track/views/event_menus.xml b/addons/website_event_track/views/event_menus.xml
index cec7203d774..8ec7b7b2d95 100644
--- a/addons/website_event_track/views/event_menus.xml
+++ b/addons/website_event_track/views/event_menus.xml
@@ -24,6 +24,13 @@
groups="base.group_no_one"
sequence="32"/>
+
+
+
+
+
diff --git a/addons/website_event_track_online/views/event_templates_registration.xml b/addons/website_event_track/views/event_templates.xml
similarity index 91%
rename from addons/website_event_track_online/views/event_templates_registration.xml
rename to addons/website_event_track/views/event_templates.xml
index 67afcd812ba..891c374de1a 100644
--- a/addons/website_event_track_online/views/event_templates_registration.xml
+++ b/addons/website_event_track/views/event_templates.xml
@@ -20,7 +20,7 @@
-
+
@@ -30,7 +30,7 @@
-
+
diff --git a/addons/website_event_track/views/event_track_stage_views.xml b/addons/website_event_track/views/event_track_stage_views.xml
index 833f3c66472..9ee7adf3c2b 100644
--- a/addons/website_event_track/views/event_track_stage_views.xml
+++ b/addons/website_event_track/views/event_track_stage_views.xml
@@ -26,6 +26,7 @@
+
@@ -43,6 +44,9 @@
+
+
+
diff --git a/addons/website_event_track/views/event_track_tag_views.xml b/addons/website_event_track/views/event_track_tag_views.xml
index 7885bdea14f..780b9814b8b 100644
--- a/addons/website_event_track/views/event_track_tag_views.xml
+++ b/addons/website_event_track/views/event_track_tag_views.xml
@@ -1,7 +1,44 @@
+
+ event.track.tag.category.view.form
+ event.track.tag.category
+
+
+
+
+
+
+ event.track.tag.category.view.list
+ event.track.tag.category
+
+
+
+
+
+
+
+
+
+
+ Track Tag Categories
+ event.track.tag.category
+ tree,form
+
-
Track Tags
event.track.tag
@@ -10,6 +47,8 @@
+
+
@@ -21,7 +60,10 @@
event.track.tag
+
+
+
diff --git a/addons/website_event_track/views/event_track_templates_agenda.xml b/addons/website_event_track/views/event_track_templates_agenda.xml
index 9864d568900..d7ddf00da0b 100644
--- a/addons/website_event_track/views/event_track_templates_agenda.xml
+++ b/addons/website_event_track/views/event_track_templates_agenda.xml
@@ -1,76 +1,194 @@
-
+
+
-
-
diff --git a/addons/website_event_track/views/event_track_templates_list.xml b/addons/website_event_track/views/event_track_templates_list.xml
index 43c9a6efe3d..35cdf9d24ca 100644
--- a/addons/website_event_track/views/event_track_templates_list.xml
+++ b/addons/website_event_track/views/event_track_templates_list.xml
@@ -1,7 +1,7 @@
-
+
+
+
+
+
+
+ unpublished
+
+
+
+
+
diff --git a/addons/website_event_track/views/event_track_templates_misc.xml b/addons/website_event_track/views/event_track_templates_misc.xml
index b880c7db8c5..398390513fd 100644
--- a/addons/website_event_track/views/event_track_templates_misc.xml
+++ b/addons/website_event_track/views/event_track_templates_misc.xml
@@ -4,26 +4,31 @@