Начальное наполнение

This commit is contained in:
parent e684fc28c5
commit 6c6c826d7b
145 changed files with 177179 additions and 1 deletions

View File

@ -1,2 +1,38 @@
# website_event_track
Organize Events, Trainings & Webinars
-------------------------------------
### Schedule, Promote, Sell, Organize
Get extra features per event; multiple pages, sponsors, multiple talks, talk proposal form, agenda, event-related news, documents (slides of presentations), event-specific menus.
Organize Your Tracks
--------------------
### From the talk proposal to the publication
Add a talk proposal form on your events to allow visitors to submit talks and speakers. Organize the validation process of every talk, and schedule easily.
Odoo's unique frontend and backend integration makes organization and publication so easy. Easily design beautiful speaker biographies and talks description.
Agenda and List of Talks
------------------------
### A strong user interface
Get a beautiful agenda for each event published automatically on your website. Allow your visitors to easily search and browse talks, filter by tags, locations or speakers.
Manage Sponsors
---------------
### Sell sponsorship, promote your sponsors
Add sponsors to your events and publish sponsors per level (e.g. bronze, silver, gold) on the bottom of every page of the event.
Sell sponsorship packages online through the Odoo eCommerce for a full sales cycle integration.
Communicate Efficiently
-----------------------
### Activate a blog for some events
You can activate a blog for each event allowing you to communicate on specific events. Visitors can subscribe to news to get informed.

5
__init__.py Normal file
View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models

61
__manifest__.py Normal file
View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Advanced Events',
'category': 'Marketing',
'summary': 'Sponsors, Tracks, Agenda, Event News',
'version': '1.3',
'depends': ['website_event'],
'data': [
'security/ir.model.access.csv',
'security/event_track_security.xml',
'data/event_data.xml',
'data/mail_template_data.xml',
'data/mail_templates.xml',
'data/mail_message_subtype_data.xml',
'data/event_track_data.xml',
'views/event_templates.xml',
'views/event_track_templates_agenda.xml',
'views/event_track_templates_list.xml',
'views/event_track_templates_reminder.xml',
'views/event_track_templates_page.xml',
'views/event_track_templates_proposal.xml',
'views/event_track_views.xml',
'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_event_views.xml',
'views/event_type_views.xml',
'views/res_config_settings_view.xml',
'views/website_visitor_views.xml',
'views/event_menus.xml',
'views/snippets.xml',
],
'demo': [
'data/event_demo.xml',
'data/event_track_location_demo.xml',
'data/event_track_tag_demo.xml',
'data/event_track_demo.xml',
'data/event_track_demo_description.xml',
'data/event_track_visitor_demo.xml',
],
'assets': {
'web.assets_frontend': [
'website_event_track/static/src/scss/event_track_templates.scss',
'website_event_track/static/src/scss/event_track_templates_online.scss',
'website_event_track/static/src/scss/pwa_frontend.scss',
'website_event_track/static/src/js/website_event_track.js',
'website_event_track/static/src/js/website_event_track_proposal_form.js',
'website_event_track/static/src/js/website_event_track_proposal_form_tags.js',
'website_event_track/static/src/js/event_track_reminder.js',
'website_event_track/static/src/js/event_track_timer.js',
'website_event_track/static/src/js/website_event_pwa_widget.js',
'website_event_track/static/lib/idb-keyval/idb-keyval.js',
'website_event_track/static/src/xml/event_track_proposal_templates.xml',
'website_event_track/static/src/xml/website_event_pwa.xml',
],
},
'license': 'LGPL-3',
}

6
controllers/__init__.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event
from . import event_track
from . import webmanifest

12
controllers/event.py Normal file
View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.website_event.controllers.main import WebsiteEventController
class EventOnlineController(WebsiteEventController):
def _get_registration_confirm_values(self, event, attendees_sudo):
values = super(EventOnlineController, self)._get_registration_confirm_values(event, attendees_sudo)
values['hide_sponsors'] = True
return values

528
controllers/event_track.py Normal file
View File

@ -0,0 +1,528 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from ast import literal_eval
from collections import defaultdict
from datetime import timedelta
from pytz import timezone, utc
from werkzeug.exceptions import Forbidden, NotFound
import babel
import babel.dates
import base64
import json
import operator
import pytz
from odoo import exceptions, http, fields, tools, _
from odoo.http import request
from odoo.osv import expression
from odoo.tools import is_html_empty, plaintext2html
from odoo.tools.misc import babel_locale_parse
class EventTrackController(http.Controller):
def _get_event_tracks_agenda_domain(self, event):
""" Base domain for displaying track names (preview). The returned search
domain will select the tracks that belongs to a track stage that should
be visible in the agenda (see: 'is_visible_in_agenda'). Published tracks
are also displayed whatever their stage. """
agenda_domain = [
'&',
('event_id', '=', event.id),
'|',
('is_published', '=', True),
('stage_id.is_visible_in_agenda', '=', True),
]
return agenda_domain
def _get_event_tracks_domain(self, event):
""" Base domain for displaying tracks. The returned search domain will
select the tracks that belongs to a track stage that should be visible
in the agenda (see: 'is_visible_in_agenda'). When the user is a visitor,
the domain will contain an additional condition that will remove the
unpublished tracks from the search results."""
search_domain_base = self._get_event_tracks_agenda_domain(event)
if not request.env.user.has_group('event.group_event_registration_desk'):
search_domain_base = expression.AND([
search_domain_base,
[('is_published', '=', True)]
])
return search_domain_base
# ------------------------------------------------------------
# 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, **searches):
""" Main route
:param event: event whose tracks are about to be displayed;
:param tag: deprecated: search for a specific tag
:param searches: frontend search dict, containing
* 'search': search string;
* 'tags': list of tag IDs for filtering;
"""
return request.render(
"website_event_track.tracks_session",
self._event_tracks_get_values(event, tag=tag, **searches)
)
def _event_tracks_get_values(self, event, tag=None, **searches):
# init and process search terms
searches.setdefault('search', '')
searches.setdefault('search_wishlist', '')
searches.setdefault('tags', '')
search_domain = self._get_event_tracks_agenda_domain(event)
# search on content
if searches.get('search'):
search_domain = expression.AND([
search_domain,
[('name', 'ilike', searches['search'])]
])
# search on tags
search_tags = self._get_search_tags(searches['tags'])
if not search_tags and tag: # backward compatibility
search_tags = tag
if search_tags:
# Example: You filter on age: 10-12 and activity: football.
# Doing it this way allows to only get events who are tagged "age: 10-12" AND "activity: football".
# Add another tag "age: 12-15" to the search and it would fetch the ones who are tagged:
# ("age: 10-12" OR "age: 12-15") AND "activity: football
grouped_tags = dict()
for search_tag in search_tags:
grouped_tags.setdefault(search_tag.category_id, list()).append(search_tag)
search_domain_items = [
[('tag_ids', 'in', [tag.id for tag in grouped_tags[group]])]
for group in grouped_tags
]
search_domain = expression.AND([
search_domain,
*search_domain_items
])
# fetch data to display with TZ set for both event and tracks
now_tz = utc.localize(fields.Datetime.now().replace(microsecond=0), is_dst=False).astimezone(timezone(event.date_tz))
today_tz = now_tz.date()
event = event.with_context(tz=event.date_tz or 'UTC')
tracks_sudo = event.env['event.track'].sudo().search(search_domain, order='is_published desc, date asc')
tag_categories = request.env['event.track.tag.category'].sudo().search([])
# filter on wishlist (as post processing due to costly search on is_reminder_on)
if searches.get('search_wishlist'):
tracks_sudo = tracks_sudo.filtered(lambda track: track.is_reminder_on)
# organize categories for display: announced, live, soon and day-based
tracks_announced = tracks_sudo.filtered(lambda track: not track.date)
tracks_wdate = tracks_sudo - tracks_announced
date_begin_tz_all = list(set(
dt.date()
for dt in self._get_dt_in_event_tz(tracks_wdate.mapped('date'), event)
))
date_begin_tz_all.sort()
tracks_sudo_live = tracks_wdate.filtered(lambda track: track.is_track_live)
tracks_sudo_soon = tracks_wdate.filtered(lambda track: not track.is_track_live and track.is_track_soon)
tracks_by_day = []
for display_date in date_begin_tz_all:
matching_tracks = tracks_wdate.filtered(lambda track: self._get_dt_in_event_tz([track.date], event)[0].date() == display_date)
tracks_by_day.append({'date': display_date, 'name': display_date, 'tracks': matching_tracks})
if tracks_announced:
tracks_announced = tracks_announced.sorted('wishlisted_by_default', reverse=True)
tracks_by_day.append({'date': False, 'name': _('Coming soon'), 'tracks': tracks_announced})
for tracks_group in tracks_by_day:
# the tracks group is folded if all tracks are done (and if it's not "today")
tracks_group['default_collapsed'] = (today_tz != tracks_group['date']) and all(
track.is_track_done and not track.is_track_live
for track in tracks_group['tracks']
)
# return rendering values
return {
# event information
'event': event,
'main_object': event,
# tracks display information
'tracks': tracks_sudo,
'tracks_by_day': tracks_by_day,
'tracks_live': tracks_sudo_live,
'tracks_soon': tracks_sudo_soon,
'today_tz': today_tz,
# search information
'searches': searches,
'search_count': len(tracks_sudo),
'search_key': searches['search'],
'search_wishlist': searches['search_wishlist'],
'search_tags': search_tags,
'tag_categories': tag_categories,
# environment
'is_html_empty': is_html_empty,
'hostname': request.httprequest.host.split(':')[0],
'is_event_user': request.env.user.has_group('event.group_event_user'),
}
# ------------------------------------------------------------
# AGENDA VIEW
# ------------------------------------------------------------
@http.route(['''/event/<model("event.event"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
def event_agenda(self, event, tag=None, **post):
event = event.with_context(tz=event.date_tz or 'UTC')
vals = {
'event': event,
'main_object': event,
'tag': tag,
'is_event_user': request.env.user.has_group('event.group_event_user'),
}
vals.update(self._prepare_calendar_values(event))
return request.render("website_event_track.agenda_online", vals)
def _prepare_calendar_values(self, event):
""" This methods slit the day (max end time - min start time) into
15 minutes time slots. For each time slot, we assign the tracks that
start at this specific time slot, and we add the number of time slot
that the track covers (track duration / 15 min). The calendar will be
divided into rows of 15 min, and the talks will cover the corresponding
number of rows (15 min slots). """
event = event.with_context(tz=event.date_tz or 'UTC')
local_tz = pytz.timezone(event.date_tz or 'UTC')
lang_code = request.env.context.get('lang')
base_track_domain = expression.AND([
self._get_event_tracks_agenda_domain(event),
[('date', '!=', False)]
])
tracks_sudo = request.env['event.track'].sudo().search(base_track_domain)
locations = list(set(track.location_id for track in tracks_sudo))
locations.sort(key=operator.itemgetter('sequence', 'id'))
# First split day by day (based on start time)
time_slots_by_tracks = {track: self._split_track_by_days(track, local_tz) for track in tracks_sudo}
# extract all the tracks time slots
track_time_slots = set().union(*(time_slot.keys() for time_slot in [time_slots for time_slots in time_slots_by_tracks.values()]))
# extract unique days
days = list(set(time_slot.date() for time_slot in track_time_slots))
days.sort()
# Create the dict that contains the tracks at the correct time_slots / locations coordinates
tracks_by_days = dict.fromkeys(days, 0)
time_slots_by_day = dict((day, dict(start=set(), end=set())) for day in days)
tracks_by_rounded_times = dict((time_slot, dict((location, {}) for location in locations)) for time_slot in track_time_slots)
for track, time_slots in time_slots_by_tracks.items():
start_date = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz)
end_date = start_date + timedelta(hours=(track.duration or 0.25))
for time_slot, duration in time_slots.items():
tracks_by_rounded_times[time_slot][track.location_id][track] = {
'rowspan': duration, # rowspan
'start_date': self._get_locale_time(start_date, lang_code),
'end_date': self._get_locale_time(end_date, lang_code),
'occupied_cells': self._get_occupied_cells(track, duration, locations, local_tz)
}
# get all the time slots by day to determine the max duration of a day.
day = time_slot.date()
time_slots_by_day[day]['start'].add(time_slot)
time_slots_by_day[day]['end'].add(time_slot+timedelta(minutes=15*duration))
tracks_by_days[day] += 1
# split days into 15 minutes time slots
global_time_slots_by_day = dict((day, {}) for day in days)
for day, time_slots in time_slots_by_day.items():
start_time_slot = min(time_slots['start'])
end_time_slot = max(time_slots['end'])
time_slots_count = int(((end_time_slot - start_time_slot).total_seconds() / 3600) * 4)
current_time_slot = start_time_slot
for i in range(0, time_slots_count + 1):
global_time_slots_by_day[day][current_time_slot] = tracks_by_rounded_times.get(current_time_slot, {})
global_time_slots_by_day[day][current_time_slot]['formatted_time'] = self._get_locale_time(current_time_slot, lang_code)
current_time_slot = current_time_slot + timedelta(minutes=15)
# count the number of tracks by days
tracks_by_days = dict.fromkeys(days, 0)
locations_by_days = defaultdict(list)
for track in tracks_sudo:
track_day = fields.Datetime.from_string(track.date).replace(tzinfo=pytz.utc).astimezone(local_tz).date()
tracks_by_days[track_day] += 1
if track.location_id not in locations_by_days[track_day]:
locations_by_days[track_day].append(track.location_id)
for used_locations in locations_by_days.values():
used_locations.sort(key=operator.itemgetter('sequence', 'id'))
return {
'days': days,
'tracks_by_days': tracks_by_days,
'locations_by_days': locations_by_days,
'time_slots': global_time_slots_by_day,
'locations': locations # TODO: clean me in master, kept for retro-compatibility
}
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", "[('website_track', '=', True)]"):event>/track/<model("event.track", "[('event_id', '=', event.id)]"):track>''',
type='http', auth="public", website=True, sitemap=True)
def event_track_page(self, event, track, **options):
track = self._fetch_track(track.id, allow_sudo=False)
return request.render(
"website_event_track.event_track_main",
self._event_track_page_get_values(event, track.sudo(), **options)
)
def _event_track_page_get_values(self, event, track, **options):
track = track.sudo()
option_widescreen = options.get('widescreen', False)
option_widescreen = bool(option_widescreen) if option_widescreen != '0' else False
# search for tracks list
tracks_other = track._get_track_suggestions(
restrict_domain=self._get_event_tracks_domain(track.event_id),
limit=10
)
return {
# event information
'event': event,
'main_object': track,
'track': track,
# sidebar
'tracks_other': tracks_other,
# options
'option_widescreen': option_widescreen,
# environment
'is_html_empty': is_html_empty,
'hostname': request.httprequest.host.split(':')[0],
'is_event_user': request.env.user.has_group('event.group_event_user'),
'user_event_manager': request.env.user.has_group('event.group_event_manager'),
}
@http.route("/event/track/toggle_reminder", type="json", auth="public", website=True)
def track_reminder_toggle(self, track_id, set_reminder_on):
""" Set a reminder a track for current visitor. Track visitor is created or updated
if it already exists. Exception made if un-favoriting and no track_visitor
record found (should not happen unless manually done).
:param boolean set_reminder_on:
If True, set as a favorite, otherwise un-favorite track;
If the track is a Key Track (wishlisted_by_default):
if set_reminder_on = False, blacklist the track_partner
otherwise, un-blacklist the track_partner
"""
track = self._fetch_track(track_id, allow_sudo=True)
force_create = set_reminder_on or track.wishlisted_by_default
event_track_partner = track._get_event_track_visitors(force_create=force_create)
if not track.wishlisted_by_default:
if not event_track_partner or event_track_partner.is_wishlisted == set_reminder_on: # ignore if new state = old state
return {'error': 'ignored'}
event_track_partner.is_wishlisted = set_reminder_on
else:
if not event_track_partner or event_track_partner.is_blacklisted != set_reminder_on: # ignore if new state = old state
return {'error': 'ignored'}
event_track_partner.is_blacklisted = not set_reminder_on
result = {'reminderOn': set_reminder_on}
return result
# ------------------------------------------------------------
# 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):
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():
return json.dumps({'error': 'forbidden'})
# Only accept existing tag indices. Use search instead of browse + exists:
# this prevents users to register colorless tags if not allowed to (ACL).
input_tag_indices = [int(tag_id) for tag_id in post['tags'].split(',') if tag_id]
valid_tag_indices = request.env['event.track.tag'].search([('id', 'in', input_tag_indices)]).ids
contact = request.env['res.partner']
visitor_partner = request.env['website.visitor']._get_visitor_from_request().partner_id
# Contact name is required. Therefore, empty contacts are not considered here. At least one of contact_phone
# and contact_email must be filled. Email is verified. If the post tries to create contact with no valid entry,
# raise exception. If normalized email is the same as logged partner, use its partner_id on track instead.
# This prevents contact duplication. Otherwise, create new contact with contact additional info of post.
if post.get('add_contact_information'):
valid_contact_email = tools.email_normalize(post.get('contact_email'))
# Here, the phone is not formatted. To format it, one needs a country. Based on a country, from geoip for instance.
# The problem is that one could propose a track in country A with phone number of country B. Validity is therefore
# quite tricky. We accept any format of contact_phone. Could be improved with select country phone widget.
if valid_contact_email or post.get('contact_phone'):
if visitor_partner and valid_contact_email == visitor_partner.email_normalized:
contact = visitor_partner
else:
contact = request.env['res.partner'].sudo().create({
'email': valid_contact_email,
'name': post.get('contact_name'),
'phone': post.get('contact_phone'),
})
else:
return json.dumps({'error': 'invalidFormInputs'})
# If the speaker email is the same as logged user's, then also uses its partner on track, same as above.
else:
valid_speaker_email = tools.email_normalize(post['partner_email'])
if visitor_partner and valid_speaker_email == visitor_partner.email_normalized:
contact = visitor_partner
track = request.env['event.track'].with_context({'mail_create_nosubscribe': True}).sudo().create({
'name': post['track_name'],
'partner_id': contact.id,
'partner_name': post['partner_name'],
'partner_email': post['partner_email'],
'partner_phone': post['partner_phone'],
'partner_function': post['partner_function'],
'contact_phone': contact.phone,
'contact_email': contact.email,
'event_id': event.id,
'tag_ids': [(6, 0, valid_tag_indices)],
'description': plaintext2html(post['description']),
'partner_biography': plaintext2html(post['partner_biography']),
'user_id': False,
'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)
return json.dumps({'success': True})
# ACL : This route is necessary since rpc search_read method in js is not accessible to all users (e.g. public user).
@http.route(['''/event/track_tag/search_read'''], type='json', auth="public", website=True)
def website_event_track_fetch_tags(self, domain, fields):
return request.env['event.track.tag'].search_read(domain, fields)
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def _fetch_track(self, track_id, allow_sudo=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:
if not allow_sudo:
raise Forbidden()
track = track.sudo()
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
def _get_search_tags(self, tag_search):
# TDE FIXME: make me generic (slides, event, ...)
try:
tag_ids = literal_eval(tag_search)
except Exception:
tags = request.env['event.track.tag'].sudo()
else:
# perform a search to filter on existing / valid tags implicitly
tags = request.env['event.track.tag'].sudo().search([('id', 'in', tag_ids)])
return tags
def _get_dt_in_event_tz(self, datetimes, event):
tz_name = event.date_tz
return [
utc.localize(dt, is_dst=False).astimezone(timezone(tz_name))
for dt in datetimes
]

View File

@ -0,0 +1,67 @@
# -*- 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.tools import ustr
from odoo.tools.misc import file_open
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.
"""
website = request.website
manifest = {
'name': website.events_app_name,
'short_name': website.events_app_name,
'description': _('%s Online Events Application') % website.company_id.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
"""
with file_open('website_event_track/static/src/js/service_worker.js', 'r') as fp:
body = fp.read()
js_cdn_url = 'undefined'
if request.website.cdn_activated:
cdn_url = request.website.cdn_url.replace('"','%22').replace('\x5c','%5C')
js_cdn_url = '"%s"' % cdn_url
body = body.replace('__ODOO_CDN_URL__', js_cdn_url)
response = request.make_response(body, [
('Content-Type', 'text/javascript'),
('Service-Worker-Allowed', url_for('/event')),
])
return response
@http.route('/event/offline', type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def offline(self):
""" Returns the offline page used by the 'website_event' PWA
"""
return request.render('website_event_track.pwa_offline')

10
data/event_data.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Update the event type if exists -->
<function model="event.type" name="write">
<value eval="[ref('event.event_type_data_conference', False)]"/>
<value eval="{'website_menu': True, 'website_track': True, 'website_track_proposal': True}"/>
</function>
</data>
</odoo>

13
data/event_demo.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event.event_0" model="event.event">
<field name="website_menu" eval="True"/>
<field name="website_track" eval="True"/>
<field name="website_track_proposal" eval="True"/>
</record>
<record id="event.event_7" model="event.event">
<field name="website_menu" eval="True"/>
<field name="website_track" eval="True"/>
<field name="website_track_proposal" eval="True"/>
</record>
</odoo>

41
data/event_track_data.xml Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="event_track_stage0" model="event.track.stage">
<field name="name">Proposal</field>
<field name="sequence">1</field>
<field name="color">1</field>
</record>
<record id="event_track_stage1" model="event.track.stage">
<field name="name">Confirmed</field>
<field name="sequence">2</field>
<field name="mail_template_id" ref="mail_template_data_track_confirmation"/>
<field name="color">2</field>
</record>
<record id="event_track_stage2" model="event.track.stage">
<field name="name">Announced</field>
<field name="sequence">3</field>
<field name="color">3</field>
<field name="is_visible_in_agenda" 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_visible_in_agenda" eval="True"/>
<field name="is_fully_accessible" eval="True"/>
</record>
<record id="event_track_stage4" model="event.track.stage">
<field name="name">Refused</field>
<field name="sequence">5</field>
<field name="color">5</field>
<field name="fold" eval="True"/>
</record>
<record id="event_track_stage5" model="event.track.stage">
<field name="name">Cancelled</field>
<field name="sequence">6</field>
<field name="fold" eval="True"/>
<field name="is_cancel" eval="True"/>
</record>
</data>
</odoo>

766
data/event_track_demo.xml Normal file
View File

@ -0,0 +1,766 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event_track1" model="event.track">
<field name="name">How to design a new piece of furniture</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location5"/>
<field name="duration" eval="1"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage0"/>
<field name="kanban_state">blocked</field>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track2" model="event.track">
<field name="name">How to integrate hardware materials in your pieces of furniture</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=2, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location5"/>
<field name="duration" eval="0.25"/>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="kanban_state">done</field>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track3" model="event.track">
<field name="name">Portfolio presentation</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=2, minutes=35)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location5"/>
<field name="duration" eval="0.3"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track4" model="event.track">
<field name="name">How to develop automated processes</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=3, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location5"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track5" model="event.track">
<field name="name">The new way to promote your creations</field>
<field name="is_published" eval="False"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=4, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location6"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track6" model="event.track">
<field name="name">Detailed roadmap of our new products</field>
<field name="is_published" eval="False"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=7, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location6"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track7" model="event.track">
<field name="name">A technical explanation of how to use computer design apps</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=10, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location6"/>
<field name="duration" eval="1"/>
<field name="partner_id" ref="base.res_partner_1"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track8" model="event.track">
<field name="name">How to optimize your sales, from leads to sales orders</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location7"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="kanban_state">blocked</field>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track9" model="event.track">
<field name="name">How to improve your quality processes</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="user_id" ref="base.user_admin"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=3, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location7"/>
<field name="duration" eval="1"/>
<field name="partner_id" ref="base.res_partner_12"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track10" model="event.track">
<field name="name">Raising qualitive insights from your customers</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=5, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location7"/>
<field name="duration" eval="0.5"/>
<field name="stage_id" ref="event_track_stage0"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track11" model="event.track">
<field name="name">Discover our new design team</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=10, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location7"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track12" model="event.track">
<field name="name">Latest trends</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location7"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track13" model="event.track">
<field name="name">Advanced reporting</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=2, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location8"/>
<field name="duration" eval="0.25"/>
<field name="partner_id" ref="base.res_partner_12"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track14" model="event.track">
<field name="name">Partnership programs</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=3, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location8"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_10"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track15" model="event.track">
<field name="name">How to communicate with your community</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=8, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location8"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track16" model="event.track">
<field name="name">How to follow us on the social media</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=12, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location8"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_12"/>
<field name="stage_id" ref="event_track_stage0"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track17" model="event.track">
<field name="name">The new marketing strategy</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_10"/>
<field name="stage_id" ref="event_track_stage0"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track18" model="event.track">
<field name="name">How to build your marketing strategy within a competitive environment</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track19" model="event.track">
<field name="name">Advanced lead management: tips and tricks from the fields</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=3, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track20" model="event.track">
<field name="name">New Certification Program</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=3, hours=5, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track21" model="event.track">
<field name="name">House of World Cultures</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=4, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track22" model="event.track">
<field name="name">Minimal but efficient design</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=4, hours=4, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="stage_id" ref="event_track_stage0"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track23" model="event.track">
<field name="name">Key Success factors selling our furniture</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=4, hours=5, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location9"/>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_1"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track24" model="event.track">
<field name="name">Design contest (entire day)</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=1, minutes=25)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location10"/>
<field name="duration" eval="1.5"/>
<field name="partner_id" ref="base.res_partner_18"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track25" model="event.track">
<field name="name">Design contest (entire afternoon)</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=4, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="location_id" ref="website_event_track.event_track_location10"/>
<field name="duration" eval="3.5"/>
<field name="partner_id" ref="base.res_partner_18"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track27" model="event.track">
<field name="name">My Company global presentation</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=3, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="duration" eval="1"/>
<field name="partner_id" ref="base.res_partner_1"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track28" model="event.track">
<field name="name">Status &amp; Strategy</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1, hours=4, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="duration" eval="0.5"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track29" model="event.track">
<field name="name">The new marketing strategy</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=1, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="duration" eval="0.25"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track30" model="event.track">
<field name="name">Morning break</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=4, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="duration" eval="0.25"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<record id="event_track31" model="event.track">
<field name="name">Lunch</field>
<field name="is_published" eval="True"/>
<field name="event_id" ref="event.event_0"/>
<field name="date" eval="(DateTime.now() + timedelta(days=2, hours=16, minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"></field>
<field name="duration" eval="1"/>
<field name="stage_id" ref="event_track_stage1"/>
<field name="user_id" ref="base.user_admin"/>
</record>
<!-- Tracks of: "OpenWood: Furniture Collection Online Reveal" -->
<!-- DAY 1 -->
<record id="event_7_track_1" model="event.track">
<field name="name">What This Event Is All About</field>
<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')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">2</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_15"/>
<field name="website_cta" eval="True"/>
<field name="website_cta_title">Try Now</field>
<field name="website_cta_url">http://www.example.com</field>
<field name="website_cta_delay">10</field>
</record>
<record id="event_7_track_2" model="event.track">
<field name="name">First Day Wrapup</field>
<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 16:00:00')"></field>
<field name="tag_ids" eval="[
(4, ref('website_event_track.event_track_tag1')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<!-- Location 1 -->
<record id="event_7_track_3" model="event.track">
<field name="name">Easy Way To Build a Wooden House</field>
<field name="color">2</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 08:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">2.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="event_7_track_4" model="event.track">
<field name="name">Life at Home Around the World: Williams Story</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage4"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 12:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="False"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_15"/>
<field name="website_cta" eval="True"/>
<field name="website_cta_title">Try Now</field>
<field name="website_cta_url">http://www.example.com</field>
<field name="website_cta_delay">10</field>
</record>
<record id="event_7_track_5" model="event.track">
<field name="name">Top 10 Most Expensive Wood in the World</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 14:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<!-- Location 2 -->
<record id="event_7_track_6" model="event.track">
<field name="name">Securing your Lumber during transport</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 08:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="event_7_track_7" model="event.track">
<field name="name">Woodworking: How I got started!</field>
<field name="color">3</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 10:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag2')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="event_7_track_8" model="event.track">
<field name="name">Dealing with OpenWood Furniture</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 12:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="False"/>
<field name="duration">2</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<record id="event_7_track_9" model="event.track">
<field name="name">Kitchens for the Future</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 14:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="False"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<!-- Location 3 -->
<record id="event_7_track_l3_1" model="event.track">
<field name="name">Voice from Customer</field>
<field name="color">5</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 08:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="False"/>
<field name="duration">2</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="event_7_track_l3_2" model="event.track">
<field name="name">Who's OpenWood anyway?</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="website_event_track.event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="(DateTime.now() - timedelta(days=1)).strftime('%Y-%m-%d 13:00:00')"></field>
<field name="duration">1.5</field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_18"/>
</record>
<!-- DAY 2 -->
<record id="event_7_track_10" model="event.track">
<field name="name">Welcome to Day 2</field>
<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')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="event_7_track_11" model="event.track">
<field name="name">Day 2 Wrapup</field>
<field name="color">1</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 16:00:00')"></field>
<field name="tag_ids" eval="[
(4, ref('website_event_track.event_track_tag1')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<!-- Location 1 -->
<record id="event_7_track_12" model="event.track">
<field name="name">Climate positive</field>
<field name="color">3</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 08:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">3</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_15"/>
</record>
<record id="event_7_track_13" model="event.track">
<field name="name">Log House Building</field>
<field name="color">0</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'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="event_7_track_14" model="event.track">
<field name="name">Building a DIY cabin from the ground up</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<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_tag3'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<!-- Location 2 -->
<record id="event_7_track_15" model="event.track">
<field name="name">Logs to lumber</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 08:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="event_7_track_16" model="event.track">
<field name="name">Pretty. Ugly. Lovely.</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 09:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag2')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">2</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_16"/>
</record>
<record id="event_7_track_17" model="event.track">
<field name="name">10 DIY Furniture Ideas For Absolute Beginners</field>
<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 12:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="event_7_track_18" model="event.track">
<field name="name">6 Woodworking tips and tricks for beginners</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 13:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
<record id="event_7_track_19" model="event.track">
<field name="name">DIY Timber Cladding Project</field>
<field name="color">7</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="DateTime.now().strftime('%Y-%m-%d 14:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<!-- Location 3 -->
<record id="event_7_track_l3_10" model="event.track">
<field name="name">Live Testimonial</field>
<field name="color">5</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="website_event_track.event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="DateTime.now() - timedelta(minutes=30)"></field>
<field name="duration">0.75</field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_31"/>
</record>
<record id="event_7_track_l3_11" model="event.track">
<field name="name">Happy with OpenWood</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="website_event_track.event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="DateTime.now() + timedelta(minutes=15)"></field>
<field name="duration">0.75</field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<!-- DAY 3 -->
<record id="event_7_track_20" model="event.track">
<field name="name">Our Last Day Together!</field>
<field name="color">1</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<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')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_28"/>
</record>
<record id="event_7_track_21" model="event.track">
<field name="name">Event Wrapup</field>
<field name="color">1</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 15:00:00')"></field>
<field name="tag_ids" eval="[
(4, ref('website_event_track.event_track_tag1')),
(4, ref('website_event_track.event_track_tag2')),
(4, ref('website_event_track.event_track_tag3')),
(4, ref('website_event_track.event_track_tag13'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_3"/>
</record>
<!-- Location 1 -->
<record id="event_7_track_22" model="event.track">
<field name="name">Tools for the Woodworking Beginner</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 08:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag11'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">2</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<record id="event_7_track_23" model="event.track">
<field name="name">Restoring Old Woodworking Tools</field>
<field name="color">7</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_1"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 12:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag3')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1.5</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_4"/>
</record>
<!-- Location 2 -->
<record id="event_7_track_24" model="event.track">
<field name="name">Old is New</field>
<field name="color">4</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_2"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 09:00:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag2'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">1</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
<!-- Location 3 -->
<record id="event_7_track_25" model="event.track">
<field name="name">Live Testimonials</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage3"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 07:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1')), (4, ref('website_event_track.event_track_tag2')), (4, ref('website_event_track.event_track_tag12'))]"/>
<field name="is_published" eval="True"/>
<field name="duration">3</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_18"/>
</record>
<record id="event_7_track_26" model="event.track">
<field name="name">Less Furniture is More Furniture</field>
<field name="color">0</field>
<field name="event_id" ref="event.event_7"/>
<field name="stage_id" ref="event_track_stage2"/>
<field name="location_id" ref="website_event_track.event_track_location_online_3"/>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 12:30:00')"></field>
<field name="tag_ids" eval="[(4, ref('website_event_track.event_track_tag1'))]"/>
<field name="is_published" eval="False"/>
<field name="duration">0.75</field>
<field name="user_id" ref="base.user_admin"/>
<field name="partner_id" ref="base.res_partner_address_17"/>
</record>
</odoo>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event_7_track_3" model="event.track">
<field name="description" type="html">
<p>Considering to build a wooden house? Watch this video to find out more information about a construction process and final result. Step by step simple explanation! Interested?</p>
</field>
</record>
<record id="event_7_track_5" model="event.track">
<field name="description" type="html">
<p>Top most expensive wood in the world is quite interesting topic and several people may be surprised
that there are hundreds of wood types exist around the globe following different properties and use.</p>
<p>There are several variants of wood is available in the world but we are talking about most expensive
ones in the world and keeping to the point we have arranged ten most expensive wood.</p>
</field>
</record>
<record id="event_7_track_6" model="event.track">
<field name="description" type="html">
<p>Use these simple steps to easily haul LONG lumber in a short box pickup truck. A dose of carpenter's
ingenuity along with a couple boards, a sturdy strap and a few screws are all I use to easily haul
long boards from the lumberyard to the Next Level Carpentry shop or jobsite.</p>
<p>Using a unique wrapping method for a tie down strap (NOT Bungee cords!!!) allows lumber to be
cinched securely WITHOUT the need to tie and untie tricky or complicated knots.</p>
</field>
</record>
<record id="event_7_track_7" model="event.track">
<field name="description" type="html">
<p>Probably one of the most asked questions I've gotten is how I got started woodworking! In this video I share with you how/why I started building furniture!</p>
</field>
</record>
<record id="event_7_track_13" model="event.track">
<field name="description" type="html">
<p>Considering to build a wooden house? Watch this video to find out more information about a construction process and final result. Step by step simple explanation! Interested?</p>
</field>
</record>
<record id="event_7_track_15" model="event.track">
<field name="description" type="html">
<p>In this video we will see how lumber is made in a sawmill factory.</p>
</field>
</record>
<record id="event_7_track_17" model="event.track">
<field name="description" type="html">
<p> As you may have heard before, making your own furniture is actually not as difficult or as complicated as you think.
In fact, some projects are so easy anyone could successfully complete them. For example, making a cute stool out of
a old tire is a real piece of cake and if youre in need of a coffee table you can easily put one together using
wood crates.</p>
<p>There are a lot of ideas worth exploring so start with the 10 DIY furniture ideas for absolute beginners.</p>
</field>
</record>
<record id="event_7_track_18" model="event.track">
<field name="description" type="html">
<p>In this video, I covered 6 tips and tricks to help out beginners:
<ul>
<li>Making a center marking gauge</li>
<li>Bandy clamp hack</li>
<li>Right angle clamp jig</li>
<li>Miter saw tip</li>
<li>Glue tip</li>
<li>Dowel Hack</li>
</ul>
</p>
</field>
</record>
<record id="event_7_track_19" model="event.track">
<field name="description" type="html">
<p>Link to Q&amp;A here! The time has come to hide those old block walls. Love simple and transformation type projects like this! :)-</p>
</field>
</record>
<record id="event_7_track_23" model="event.track">
<field name="description" type="html">
<p> Restoring old woodworking tools</p>
</field>
</record>
</odoo>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="event_track_location5" model="event.track.location">
<field name="name">Le Foyer du lac</field>
</record>
<record id="event_track_location6" model="event.track.location">
<field name="name">Theatre</field>
</record>
<record id="event_track_location7" model="event.track.location">
<field name="name">Lauzelle</field>
</record>
<record id="event_track_location8" model="event.track.location">
<field name="name">Foyer Royal</field>
</record>
<record id="event_track_location9" model="event.track.location">
<field name="name">Biereau</field>
</record>
<record id="event_track_location10" model="event.track.location">
<field name="name">Bruyère</field>
</record>
<record id="event_track_location_online_1" model="event.track.location">
<field name="name">RD and Sales</field>
</record>
<record id="event_track_location_online_2" model="event.track.location">
<field name="name">Furniture</field>
</record>
<record id="event_track_location_online_3" model="event.track.location">
<field name="name">Live with Customers</field>
</record>
</odoo>

View File

@ -0,0 +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>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="event_7_track_2_visitor_0" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website.website_visitor_0"/>
<field name="is_wishlisted" eval="True"/>
</record>
<record id="event_7_track_2_visitor_2" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website.website_visitor_2"/>
<field name="is_wishlisted" eval="True"/>
</record>
</data></odoo>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<!-- Event-related subtypes for new track / Chatter -->
<record id="mt_event_track" model="mail.message.subtype">
<field name="name">New Track</field>
<field name="res_model">event.event</field>
<field name="default" eval="False"/>
</record>
<!-- Track subtypes -->
<record id="mt_track_blocked" model="mail.message.subtype">
<field name="name">Track Blocked</field>
<field name="res_model">event.track</field>
<field name="default" eval="False"/>
<field name="internal" eval="True"/>
<field name="description">Track blocked</field>
</record>
<record id="mt_track_ready" model="mail.message.subtype">
<field name="name">Track Ready</field>
<field name="res_model">event.track</field>
<field name="default" eval="True"/>
<field name="internal" eval="True"/>
<field name="description">Track Ready for Next Stage</field>
</record>
</data></odoo>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<record id="mail_template_data_track_confirmation" model="mail.template">
<field name="name">Event: Track Confirmation</field>
<field name="model_id" ref="website_event_track.model_event_track"/>
<field name="subject">Confirmation of {{ object.name }}</field>
<field name="use_default_to" eval="True"/>
<field name="description">Sent to speakers whose track proposal is accepted (set the template on the right stage)</field>
<field name="body_html" type="html">
<div>
Dear <t t-out="object.partner_id.name or object.partner_name or ''">Brandon Freeman</t><br/>
We are pleased to inform you that your proposal <t t-out="object.name or ''">What This Event Is All About</t> has been accepted and confirmed for the event <t t-out="object.event_id.name or ''">OpenWood Collection Online Reveal</t>.
<br/>
You will find more details here:
<div style="margin: 16px 0px 16px 0px;">
<a t-attf-href="/event/{{ object.event_id.id }}/track/{{ object.id }}"
style="padding: 8px 16px 8px 16px; font-size: 14px; color: #FFFFFF; text-decoration: none !important; background-color: #875A7B; border: 0px solid #875A7B; border-radius:3px">
View Talk
</a>
</div>
<br/><br/>
Thank you,
<t t-if="user.signature">
<br />
<t t-out="user.signature or ''">--<br/>Mitchell Admin</t>
</t>
</div></field>
<field name="lang">{{ object.partner_id.lang }}</field>
<field name="auto_delete" eval="True"/>
</record>
</data></odoo>

32
data/mail_templates.xml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Chatter templates -->
<template id="event_track_template_new">
<p>
<span>New track proposal</span>
<a href="#" t-att-data-oe-model="track._name" t-att-data-oe-id="track.id" t-out="track.name"/>
</p>
<ul>
<!-- "contact" information (the ones from partner_id) take precedence over public information -->
<t t-set="speaker_name" t-value="track.partner_id.name or track.partner_name or track.partner_email" />
<li t-if="speaker_name">
<b>Proposed By</b>: <span t-out="speaker_name"/>
</li>
<t t-set="speaker_email" t-value="track.contact_email or track.partner_email" />
<li t-if="speaker_email">
<b>Mail</b>: <a t-attf-href="mailto:#{speaker_email}" t-out="speaker_email"></a>
</li>
<li t-if="track.contact_phone or track.partner_phone">
<b>Phone</b>: <span t-out="track.contact_phone or track.partner_phone"/>
</li>
<li t-if="not is_html_empty(track.partner_biography)">
<b>Speaker Biography</b>: <div t-field="track.partner_biography"/>
</li>
<li t-if="not is_html_empty(track.description)">
<b>Talk Introduction</b>: <div t-field="track.description"/>
</li>
</ul>
</template>
</odoo>

2462
i18n/af.po Normal file

File diff suppressed because it is too large Load Diff

2630
i18n/ar.po Normal file

File diff suppressed because it is too large Load Diff

2467
i18n/az.po Normal file

File diff suppressed because it is too large Load Diff

2545
i18n/bg.po Normal file

File diff suppressed because it is too large Load Diff

2463
i18n/bs.po Normal file

File diff suppressed because it is too large Load Diff

2654
i18n/ca.po Normal file

File diff suppressed because it is too large Load Diff

2545
i18n/cs.po Normal file

File diff suppressed because it is too large Load Diff

2579
i18n/da.po Normal file

File diff suppressed because it is too large Load Diff

2669
i18n/de.po Normal file

File diff suppressed because it is too large Load Diff

2467
i18n/el.po Normal file

File diff suppressed because it is too large Load Diff

2461
i18n/en_GB.po Normal file

File diff suppressed because it is too large Load Diff

2666
i18n/es.po Normal file

File diff suppressed because it is too large Load Diff

2667
i18n/es_419.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_BO.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_CL.po Normal file

File diff suppressed because it is too large Load Diff

2462
i18n/es_CO.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_CR.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_DO.po Normal file

File diff suppressed because it is too large Load Diff

2464
i18n/es_EC.po Normal file

File diff suppressed because it is too large Load Diff

2461
i18n/es_PE.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_PY.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/es_VE.po Normal file

File diff suppressed because it is too large Load Diff

2601
i18n/et.po Normal file

File diff suppressed because it is too large Load Diff

2461
i18n/eu.po Normal file

File diff suppressed because it is too large Load Diff

2552
i18n/fa.po Normal file

File diff suppressed because it is too large Load Diff

2659
i18n/fi.po Normal file

File diff suppressed because it is too large Load Diff

2669
i18n/fr.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/fr_BE.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/fr_CA.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/gl.po Normal file

File diff suppressed because it is too large Load Diff

2466
i18n/gu.po Normal file

File diff suppressed because it is too large Load Diff

2561
i18n/he.po Normal file

File diff suppressed because it is too large Load Diff

2482
i18n/hr.po Normal file

File diff suppressed because it is too large Load Diff

2552
i18n/hu.po Normal file

File diff suppressed because it is too large Load Diff

2654
i18n/id.po Normal file

File diff suppressed because it is too large Load Diff

2458
i18n/is.po Normal file

File diff suppressed because it is too large Load Diff

2656
i18n/it.po Normal file

File diff suppressed because it is too large Load Diff

2590
i18n/ja.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/ka.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/kab.po Normal file

File diff suppressed because it is too large Load Diff

2463
i18n/km.po Normal file

File diff suppressed because it is too large Load Diff

2599
i18n/ko.po Normal file

File diff suppressed because it is too large Load Diff

2458
i18n/lb.po Normal file

File diff suppressed because it is too large Load Diff

2556
i18n/lt.po Normal file

File diff suppressed because it is too large Load Diff

2540
i18n/lv.po Normal file

File diff suppressed because it is too large Load Diff

2465
i18n/mk.po Normal file

File diff suppressed because it is too large Load Diff

2490
i18n/mn.po Normal file

File diff suppressed because it is too large Load Diff

2471
i18n/nb.po Normal file

File diff suppressed because it is too large Load Diff

2655
i18n/nl.po Normal file

File diff suppressed because it is too large Load Diff

2644
i18n/pl.po Normal file

File diff suppressed because it is too large Load Diff

2541
i18n/pt.po Normal file

File diff suppressed because it is too large Load Diff

2645
i18n/pt_BR.po Normal file

File diff suppressed because it is too large Load Diff

2475
i18n/ro.po Normal file

File diff suppressed because it is too large Load Diff

2668
i18n/ru.po Normal file

File diff suppressed because it is too large Load Diff

2542
i18n/sk.po Normal file

File diff suppressed because it is too large Load Diff

2549
i18n/sl.po Normal file

File diff suppressed because it is too large Load Diff

2460
i18n/sq.po Normal file

File diff suppressed because it is too large Load Diff

2637
i18n/sr.po Normal file

File diff suppressed because it is too large Load Diff

2464
i18n/sr@latin.po Normal file

File diff suppressed because it is too large Load Diff

2551
i18n/sv.po Normal file

File diff suppressed because it is too large Load Diff

2627
i18n/th.po Normal file

File diff suppressed because it is too large Load Diff

2646
i18n/tr.po Normal file

File diff suppressed because it is too large Load Diff

2645
i18n/uk.po Normal file

File diff suppressed because it is too large Load Diff

2606
i18n/vi.po Normal file

File diff suppressed because it is too large Load Diff

2522
i18n/website_event_track.pot Normal file

File diff suppressed because it is too large Load Diff

2595
i18n/zh_CN.po Normal file

File diff suppressed because it is too large Load Diff

2590
i18n/zh_TW.po Normal file

File diff suppressed because it is too large Load Diff

16
models/__init__.py Normal file
View File

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event_event
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 res_config_settings
from . import website
from . import website_event_menu
from . import website_menu
from . import website_visitor

91
models/event_event.py Normal file
View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.addons.http_routing.models.ir_http import slug
class Event(models.Model):
_inherit = "event.event"
track_ids = fields.One2many('event.track', 'event_id', 'Tracks')
track_count = fields.Integer('Track Count', compute='_compute_track_count')
website_track = fields.Boolean(
'Tracks on Website', compute='_compute_website_track',
readonly=False, store=True)
website_track_proposal = fields.Boolean(
'Proposals on Website', compute='_compute_website_track_proposal',
readonly=False, store=True)
track_menu_ids = fields.One2many('website.event.menu', 'event_id', string='Event Tracks Menus', domain=[('menu_type', '=', 'track')])
track_proposal_menu_ids = fields.One2many('website.event.menu', 'event_id', string='Event Proposals Menus', domain=[('menu_type', '=', 'track_proposal')])
allowed_track_tag_ids = fields.Many2many('event.track.tag', relation='event_allowed_track_tags_rel', string='Available Track Tags')
tracks_tag_ids = fields.Many2many(
'event.track.tag', relation='event_track_tags_rel', string='Track Tags',
compute='_compute_tracks_tag_ids', store=True)
def _compute_track_count(self):
data = self.env['event.track']._read_group([('stage_id.is_cancel', '!=', True)], ['event_id'], ['__count'])
result = {event.id: count for event, count in data}
for event in self:
event.track_count = result.get(event.id, 0)
@api.depends('event_type_id', 'website_menu')
def _compute_website_track(self):
""" Propagate event_type configuration (only at change); otherwise propagate
website_menu updated value. Also force True is track_proposal changes. """
for event in self:
if event.event_type_id and event.event_type_id != event._origin.event_type_id:
event.website_track = event.event_type_id.website_track
elif event.website_menu and (event.website_menu != event._origin.website_menu or not event.website_track):
event.website_track = True
elif not event.website_menu:
event.website_track = False
@api.depends('event_type_id', 'website_track')
def _compute_website_track_proposal(self):
""" Propagate event_type configuration (only at change); otherwise propagate
website_track updated value (both together True or False at update). """
for event in self:
if event.event_type_id and event.event_type_id != event._origin.event_type_id:
event.website_track_proposal = event.event_type_id.website_track_proposal
elif event.website_track != event._origin.website_track or not event.website_track or not event.website_track_proposal:
event.website_track_proposal = event.website_track
@api.depends('track_ids.tag_ids', 'track_ids.tag_ids.color')
def _compute_tracks_tag_ids(self):
for event in self:
event.tracks_tag_ids = event.track_ids.mapped('tag_ids').filtered(lambda tag: tag.color != 0).ids
# ------------------------------------------------------------
# WEBSITE MENU MANAGEMENT
# ------------------------------------------------------------
def toggle_website_track(self, val):
self.website_track = val
def toggle_website_track_proposal(self, val):
self.website_track_proposal = val
def _get_menu_update_fields(self):
return super(Event, self)._get_menu_update_fields() + ['website_track', 'website_track_proposal']
def _update_website_menus(self, menus_update_by_field=None):
super(Event, self)._update_website_menus(menus_update_by_field=menus_update_by_field)
for event in self:
if event.menu_id and (not menus_update_by_field or event in menus_update_by_field.get('website_track')):
event._update_website_menu_entry('website_track', 'track_menu_ids', 'track')
if event.menu_id and (not menus_update_by_field or event in menus_update_by_field.get('website_track_proposal')):
event._update_website_menu_entry('website_track_proposal', 'track_proposal_menu_ids', 'track_proposal')
def _get_menu_type_field_matching(self):
res = super(Event, self)._get_menu_type_field_matching()
res['track_proposal'] = 'website_track_proposal'
return res
def _get_website_menu_entries(self):
self.ensure_one()
return super(Event, self)._get_website_menu_entries() + [
(_('Talks'), '/event/%s/track' % slug(self), False, 10, 'track'),
(_('Agenda'), '/event/%s/agenda' % slug(self), False, 70, 'track'),
(_('Talk Proposals'), '/event/%s/track_proposal' % slug(self), False, 15, 'track_proposal')
]

612
models/event_track.py Normal file
View File

@ -0,0 +1,612 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import timedelta
from pytz import utc
from random import randint
from odoo import api, fields, models, tools
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):
_name = "event.track"
_description = 'Event Track'
_order = 'priority, date'
_inherit = ['mail.thread', 'mail.activity.mixin', 'website.seo.metadata', 'website.published.mixin']
@api.model
def _get_default_stage_id(self):
return self.env['event.track.stage'].search([], limit=1).id
# description
name = fields.Char('Title', required=True, translate=True)
event_id = fields.Many2one('event.event', 'Event', required=True)
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')
tag_ids = fields.Many2many('event.track.tag', string='Tags')
description = fields.Html(translate=html_translate, sanitize_attributes=False, sanitize_form=False)
color = fields.Integer('Agenda Color')
priority = fields.Selection([
('0', 'Low'), ('1', 'Medium'),
('2', 'High'), ('3', 'Highest')],
'Priority', required=True, default='1')
# management
stage_id = fields.Many2one(
'event.track.stage', string='Stage', ondelete='restrict',
index=True, copy=False, default=_get_default_stage_id,
group_expand='_read_group_stage_ids',
required=True, tracking=True)
legend_blocked = fields.Char(related='stage_id.legend_blocked',
string='Kanban Blocked Explanation', readonly=True)
legend_done = fields.Char(related='stage_id.legend_done',
string='Kanban Valid Explanation', readonly=True)
legend_normal = fields.Char(related='stage_id.legend_normal',
string='Kanban Ongoing Explanation', readonly=True)
kanban_state = fields.Selection([
('normal', 'Grey'),
('done', 'Green'),
('blocked', 'Red')], string='Kanban State',
copy=False, default='normal', required=True,
help="A track's kanban state indicates special situations affecting it:\n"
" * Grey is the default situation\n"
" * Red indicates something is preventing the progress of this track\n"
" * Green indicates the track is ready to be pulled to the next stage")
kanban_state_label = fields.Char(
string='Kanban State Label', compute='_compute_kanban_state_label', store=True,
tracking=True)
partner_id = fields.Many2one('res.partner', 'Contact')
# speaker information
partner_name = fields.Char(
string='Name', compute='_compute_partner_name',
readonly=False, store=True, tracking=10)
partner_email = fields.Char(
string='Email', compute='_compute_partner_email',
readonly=False, store=True, tracking=20)
partner_phone = fields.Char(
string='Phone', compute='_compute_partner_phone',
readonly=False, store=True, tracking=30)
partner_biography = fields.Html(
string='Biography', compute='_compute_partner_biography',
sanitize_attributes=False,
readonly=False, store=True)
partner_function = fields.Char(
'Job Position', compute='_compute_partner_function',
store=True, readonly=False)
partner_company_name = fields.Char(
'Company Name', compute='_compute_partner_company_name',
readonly=False, store=True)
partner_tag_line = fields.Char(
'Tag Line', compute='_compute_partner_tag_line',
help='Description of the partner (name, function and company name)')
image = fields.Image(
string="Speaker Photo", compute="_compute_partner_image",
readonly=False, store=True,
max_width=256, max_height=256)
# contact information
contact_email = fields.Char(
string='Contact Email', compute='_compute_contact_email',
readonly=False, store=True, tracking=20)
contact_phone = fields.Char(
string='Contact Phone', compute='_compute_contact_phone',
readonly=False, store=True, tracking=30)
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=0.5)
is_track_live = fields.Boolean(
'Is Track Live', compute='_compute_track_time_data')
is_track_soon = fields.Boolean(
'Is Track Soon', compute='_compute_track_time_data')
is_track_today = fields.Boolean(
'Is Track Today', compute='_compute_track_time_data')
is_track_upcoming = fields.Boolean(
'Is Track Upcoming', compute='_compute_track_time_data')
is_track_done = fields.Boolean(
'Is Track Done', compute='_compute_track_time_data')
track_start_remaining = fields.Integer(
'Minutes before track starts', compute='_compute_track_time_data',
help="Remaining time before track starts (seconds)")
track_start_relative = fields.Integer(
'Minutes compare to track start', compute='_compute_track_time_data',
help="Relative time compared to track start (seconds)")
# 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 set as favorite for each attendee registered to the event.""")
# Call to action
website_cta = fields.Boolean('Magic Button',
help="Display a Call to Action button to your Attendees while they watch your Track.")
website_cta_title = fields.Char('Button Title')
website_cta_url = fields.Char('Button Target URL')
website_cta_delay = fields.Integer('Show Button')
# time information for CTA
is_website_cta_live = fields.Boolean(
'Is CTA Live', compute='_compute_cta_time_data',
help="CTA button is available")
website_cta_start_remaining = fields.Integer(
'Minutes before CTA starts', compute='_compute_cta_time_data',
help="Remaining time before CTA starts (seconds)")
@api.depends('name')
def _compute_website_url(self):
super(Track, self)._compute_website_url()
for track in self:
if track.id:
track.website_url = '/event/%s/track/%s' % (slug(track.event_id), slug(track))
# STAGES
@api.depends('stage_id', 'kanban_state')
def _compute_kanban_state_label(self):
for track in self:
if track.kanban_state == 'normal':
track.kanban_state_label = track.stage_id.legend_normal
elif track.kanban_state == 'blocked':
track.kanban_state_label = track.stage_id.legend_blocked
else:
track.kanban_state_label = track.stage_id.legend_done
# SPEAKER
@api.depends('partner_id')
def _compute_partner_name(self):
for track in self:
if track.partner_id and not track.partner_name:
track.partner_name = track.partner_id.name
@api.depends('partner_id')
def _compute_partner_email(self):
for track in self:
if track.partner_id and not track.partner_email:
track.partner_email = track.partner_id.email
@api.depends('partner_id')
def _compute_partner_phone(self):
for track in self:
if track.partner_id and not track.partner_phone:
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_partner_function(self):
for track in self:
if track.partner_id and not track.partner_function:
track.partner_function = track.partner_id.function
@api.depends('partner_id', 'partner_id.company_type')
def _compute_partner_company_name(self):
for track in self:
if track.partner_id.company_type == 'company':
track.partner_company_name = track.partner_id.name
elif not track.partner_company_name:
track.partner_company_name = track.partner_id.parent_id.name
@api.depends('partner_name', 'partner_function', 'partner_company_name')
def _compute_partner_tag_line(self):
for track in self:
if not track.partner_name:
track.partner_tag_line = False
continue
tag_line = track.partner_name
if track.partner_function:
if track.partner_company_name:
tag_line = _('%(name)s, %(function)s at %(company)s',
name=track.partner_name,
function=track.partner_function,
company=track.partner_company_name
)
else:
tag_line = '%s, %s' % (track.partner_name, track.partner_function)
elif track.partner_company_name:
tag_line = _('%(name)s from %(company)s',
name=tag_line,
company=track.partner_company_name
)
track.partner_tag_line = tag_line
@api.depends('partner_id')
def _compute_partner_image(self):
for track in self:
if not track.image:
track.image = track.partner_id.image_256
# CONTACT
@api.depends('partner_id', 'partner_id.email')
def _compute_contact_email(self):
for track in self:
if track.partner_id:
track.contact_email = track.partner_id.email
@api.depends('partner_id', 'partner_id.phone')
def _compute_contact_phone(self):
for track in self:
if track.partner_id:
track.contact_phone = track.partner_id.phone
# TIME
@api.depends('date', 'duration')
def _compute_end_date(self):
for track in self:
if track.date:
delta = timedelta(minutes=60 * track.duration)
track.date_end = track.date + delta
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'],
)
visitor_ids_map = {track.id: visitor_ids for track, visitor_ids 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)]
# TIME
@api.depends('date', 'date_end')
def _compute_track_time_data(self):
""" Compute start and remaining time for track itself. Do everything in
UTC as we compute only time deltas here. """
now_utc = utc.localize(fields.Datetime.now().replace(microsecond=0))
for track in self:
if not track.date:
track.is_track_live = track.is_track_soon = track.is_track_today = track.is_track_upcoming = track.is_track_done = False
track.track_start_relative = track.track_start_remaining = 0
continue
date_begin_utc = utc.localize(track.date, is_dst=False)
date_end_utc = utc.localize(track.date_end, is_dst=False)
track.is_track_live = date_begin_utc <= now_utc < date_end_utc
track.is_track_soon = (date_begin_utc - now_utc).total_seconds() < 30*60 if date_begin_utc > now_utc else False
track.is_track_today = date_begin_utc.date() == now_utc.date()
track.is_track_upcoming = date_begin_utc > now_utc
track.is_track_done = date_end_utc <= now_utc
if date_begin_utc >= now_utc:
track.track_start_relative = int((date_begin_utc - now_utc).total_seconds())
track.track_start_remaining = track.track_start_relative
else:
track.track_start_relative = int((now_utc - date_begin_utc).total_seconds())
track.track_start_remaining = 0
@api.depends('date', 'date_end', 'website_cta', 'website_cta_delay')
def _compute_cta_time_data(self):
""" Compute start and remaining time for track itself. Do everything in
UTC as we compute only time deltas here. """
now_utc = utc.localize(fields.Datetime.now().replace(microsecond=0))
for track in self:
if not track.website_cta:
track.is_website_cta_live = track.website_cta_start_remaining = False
continue
date_begin_utc = utc.localize(track.date, is_dst=False) + timedelta(minutes=track.website_cta_delay or 0)
date_end_utc = utc.localize(track.date_end, is_dst=False)
track.is_website_cta_live = date_begin_utc <= now_utc <= date_end_utc
if date_begin_utc >= now_utc:
td = date_begin_utc - now_utc
track.website_cta_start_remaining = int(td.total_seconds())
else:
track.website_cta_start_remaining = 0
# ------------------------------------------------------------
# CRUD
# ------------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
for values in vals_list:
if values.get('website_cta_url'):
values['website_cta_url'] = self.env['res.partner']._clean_website(values['website_cta_url'])
tracks = super(Track, self).create(vals_list)
post_values = {} if self.env.user.email else {'email_from': self.env.company.catchall_formatted}
for track in tracks:
track.event_id.message_post_with_source(
'website_event_track.event_track_template_new',
render_values={
'track': track,
'is_html_empty': is_html_empty,
},
subtype_xmlid='website_event_track.mt_event_track',
**post_values,
)
track._synchronize_with_stage(track.stage_id)
return tracks
def write(self, vals):
if vals.get('website_cta_url'):
vals['website_cta_url'] = self.env['res.partner']._clean_website(vals['website_cta_url'])
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)
return res
@api.model
def _read_group_stage_ids(self, stages, domain, order):
""" Always display all stages """
return stages.search([], order=order)
def _synchronize_with_stage(self, stage):
if stage.is_fully_accessible:
self.is_published = True
elif stage.is_cancel:
self.is_published = False
# ------------------------------------------------------------
# MESSAGING
# ------------------------------------------------------------
def _message_get_default_recipients(self):
return {
track.id: {
'partner_ids': [],
'email_to': ','.join(tools.email_normalize_all(track.contact_email or track.partner_email)) or track.contact_email or track.partner_email,
'email_cc': False
} for track in self
}
def _message_get_suggested_recipients(self):
recipients = super(Track, self)._message_get_suggested_recipients()
for track in self:
if track.partner_id:
if track.partner_id not in recipients:
track._message_add_suggested_recipient(recipients, partner=track.partner_id, reason=_('Contact'))
else:
# Priority: contact information then speaker information
if track.contact_email and track.contact_email != track.partner_id.email:
track._message_add_suggested_recipient(recipients, email=track.contact_email, reason=_('Contact Email'))
if not track.contact_email and track.partner_email and track.partner_email != track.partner_id.email:
track._message_add_suggested_recipient(recipients, email=track.partner_email, reason=_('Speaker Email'))
return recipients
def _message_post_after_hook(self, message, msg_vals):
# OVERRIDE
# If no partner is set on track when sending a message, then we create one from suggested contact selected.
# If one or more have been created from chatter (Suggested Recipients) we search for the expected one and write the partner_id on track.
if msg_vals.get('partner_ids') and not self.partner_id:
# Contact(s) created from chatter set on track : we verify if at least one is the expected contact
# linked to the track. (created from contact_email if any, then partner_email if any)
main_email = self.contact_email or self.partner_email
main_email_normalized = tools.email_normalize(main_email)
new_partner = message.partner_ids.filtered(
lambda partner: partner.email == main_email or (main_email_normalized and partner.email_normalized == main_email_normalized)
)
if new_partner:
mail_email_fname = 'contact_email' if self.contact_email else 'partner_email'
if new_partner[0].email_normalized:
email_domain = (mail_email_fname, 'in', [new_partner[0].email, new_partner[0].email_normalized])
else:
email_domain = (mail_email_fname, '=', new_partner[0].email)
self.search([
('partner_id', '=', False), email_domain, ('stage_id.is_cancel', '=', False),
]).write({'partner_id': new_partner[0].id})
return super(Track, self)._message_post_after_hook(message, msg_vals)
def _track_template(self, changes):
res = super(Track, self)._track_template(changes)
track = self[0]
if 'stage_id' in changes and track.stage_id.mail_template_id:
res['stage_id'] = (track.stage_id.mail_template_id, {
'auto_delete_keep_log': False,
'composition_mode': 'comment',
'email_layout_xmlid': 'mail.mail_notification_light',
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
})
return res
def _track_subtype(self, init_values):
self.ensure_one()
if 'kanban_state' in init_values and self.kanban_state == 'blocked':
return self.env.ref('website_event_track.mt_track_blocked')
elif 'kanban_state' in init_values and self.kanban_state == 'done':
return self.env.ref('website_event_track.mt_track_ready')
return super(Track, self)._track_subtype(init_values)
# ------------------------------------------------------------
# ACTION
# ------------------------------------------------------------
def open_track_speakers_list(self):
return {
'name': _('Speakers'),
'domain': [('id', 'in', self.mapped('partner_id').ids)],
'view_mode': 'kanban,form',
'res_model': 'res.partner',
'view_id': False,
'type': 'ir.actions.act_window',
}
def get_backend_menu_id(self):
return self.env.ref('event.event_main_menu').id
# ------------------------------------------------------------
# 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
def _get_track_suggestions(self, restrict_domain=None, limit=None):
""" Returns the next tracks suggested after going to the current one
given by self. Tracks always belong to the same event.
Heuristic is
* live first;
* then ordered by start date, finished being sent to the end;
* wishlisted (manually or by default);
* tag matching with current track;
* location matching with current track;
* finally a random to have an "equivalent wave" randomly given;
:param restrict_domain: an additional domain to restrict candidates;
:param limit: number of tracks to return;
"""
self.ensure_one()
base_domain = [
'&',
('event_id', '=', self.event_id.id),
('id', '!=', self.id),
]
if restrict_domain:
base_domain = expression.AND([
base_domain,
restrict_domain
])
track_candidates = self.search(base_domain, limit=None, order='date asc')
if not track_candidates:
return track_candidates
track_candidates = track_candidates.sorted(
lambda track:
(track.is_published,
track.track_start_remaining == 0 # First get the tracks that started less than 10 minutes ago ...
and track.track_start_relative < (10 * 60)
and not track.is_track_done, # ... AND not finished
track.track_start_remaining > 0, # Then the one that will begin later (the sooner come first)
-1 * track.track_start_remaining,
track.is_reminder_on,
not track.wishlisted_by_default,
len(track.tag_ids & self.tag_ids),
track.location_id == self.location_id,
randint(0, 20),
), reverse=True
)
return track_candidates[:limit]

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class TrackLocation(models.Model):
_name = "event.track.location"
_description = 'Event Track Location'
_order = 'sequence, id'
name = fields.Char('Location', required=True)
sequence = fields.Integer(default=10, help='Define the order in which the location will appear on "Agenda" page')

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
class TrackStage(models.Model):
_name = 'event.track.stage'
_description = 'Event Track Stage'
_order = 'sequence, id'
name = fields.Char(string='Stage Name', required=True, translate=True)
sequence = fields.Integer(string='Sequence', default=1)
mail_template_id = fields.Many2one(
'mail.template', string='Email Template',
domain=[('model', '=', 'event.track')],
help="If set an email will be sent to the customer when the track reaches this step.")
# legends
color = fields.Integer(string='Color')
description = fields.Text(string='Description', translate=True)
legend_blocked = fields.Char('Red Kanban Label', default=lambda s: _('Blocked'), translate=True)
legend_done = fields.Char('Green Kanban Label', default=lambda s: _('Ready for Next Stage'), translate=True)
legend_normal = fields.Char('Grey Kanban Label', default=lambda s: _('In Progress'), translate=True)
# pipe
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_visible_in_agenda = fields.Boolean(
string='Visible in agenda', compute='_compute_is_visible_in_agenda', store=True,
help='If checked, the related tracks will be visible in the frontend.')
is_fully_accessible = fields.Boolean(
string='Fully accessible', compute='_compute_is_fully_accessible', store=True,
help='If checked, automatically publish tracks so that access links to customers are provided.')
is_cancel = fields.Boolean(string='Canceled Stage')
@api.depends('is_cancel', 'is_fully_accessible')
def _compute_is_visible_in_agenda(self):
for record in self:
if record.is_cancel:
record.is_visible_in_agenda = False
elif record.is_fully_accessible:
record.is_visible_in_agenda = True
@api.depends('is_cancel', 'is_visible_in_agenda')
def _compute_is_fully_accessible(self):
for record in self:
if record.is_cancel or not record.is_visible_in_agenda:
record.is_fully_accessible = False

27
models/event_track_tag.py Normal file
View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from random import randint
from odoo import fields, models
class TrackTag(models.Model):
_name = "event.track.tag"
_description = 'Event Track Tag'
_order = "category_id, sequence, name"
def _default_color(self):
return randint(1, 11)
name = fields.Char('Tag Name', required=True)
track_ids = fields.Many2many('event.track', string='Tracks')
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!"),
]

View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class TrackTagCategory(models.Model):
_name = "event.track.tag.category"
_description = 'Event Track Tag Category'
_order = "sequence"
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")

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class TrackVisitor(models.Model):
""" Table linking track and visitors. """
_name = 'event.track.visitor'
_description = 'Track / Visitor Link'
_table = 'event_track_visitor'
_rec_name = 'track_id'
_order = 'track_id'
partner_id = fields.Many2one(
'res.partner', string='Partner', compute='_compute_partner_id',
index=True, ondelete='set null', readonly=False, store=True)
visitor_id = fields.Many2one(
'website.visitor', string='Visitor', index=True, ondelete='cascade')
track_id = fields.Many2one(
'event.track', string='Track',
index=True, required=True, ondelete='cascade')
is_wishlisted = fields.Boolean(string="Is Wishlisted")
is_blacklisted = fields.Boolean(string="Is reminder off", help="As key track cannot be un-favorited, this field store the partner choice to remove the reminder for key tracks.")
@api.depends('visitor_id')
def _compute_partner_id(self):
for track_visitor in self:
if track_visitor.visitor_id.partner_id and not track_visitor.partner_id:
track_visitor.partner_id = track_visitor.visitor_id.partner_id
elif not track_visitor.partner_id:
track_visitor.partner_id = False

22
models/event_type.py Normal file
View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class EventType(models.Model):
_inherit = 'event.type'
website_track = fields.Boolean(
string='Tracks on Website', compute='_compute_website_track_menu_data',
readonly=False, store=True)
website_track_proposal = fields.Boolean(
string='Tracks Proposals on Website', compute='_compute_website_track_menu_data',
readonly=False, store=True)
@api.depends('website_menu')
def _compute_website_track_menu_data(self):
""" Simply activate or de-activate all menus at once. """
for event_type in self:
event_type.website_track = event_type.website_menu
event_type.website_track_proposal = event_type.website_menu

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
events_app_name = fields.Char('Events App Name', related='website_id.events_app_name', readonly=False)

59
models/website.py Normal file
View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import ImageProcess
from odoo.tools.translate import _
class Website(models.Model):
_inherit = "website"
app_icon = fields.Image(
string='Website App Icon',
compute='_compute_app_icon',
store=True,
readonly=True,
help='This field holds the image used as mobile app icon on the website (PNG format).')
events_app_name = fields.Char(
string='Events App Name',
compute='_compute_events_app_name',
store=True,
readonly=False,
help="This fields holds the Event's Progressive Web App name.")
@api.depends('name')
def _compute_events_app_name(self):
for website in self:
if not website.events_app_name:
website.events_app_name = _('%s Events') % website.name
@api.constrains('events_app_name')
def _check_events_app_name(self):
for website in self:
if not website.events_app_name:
raise ValidationError(_('"Events App Name" field is required.'))
@api.depends('favicon')
def _compute_app_icon(self):
""" Computes a squared image based on the favicon to be used as mobile webapp icon.
App Icon should be in PNG format and size of at least 512x512.
If the favicon is an SVG image, it will be skipped and the app_icon will be set to False.
"""
for website in self:
image = ImageProcess(base64.b64decode(website.favicon)) if website.favicon else None
if not (image and image.image):
website.app_icon = False
continue
w, h = image.image.size
square_size = w if w > h else h
image.crop_resize(square_size, square_size)
image.image = image.image.resize((512, 512))
image.operationsCount += 1
website.app_icon = base64.b64encode(image.image_quality(output_format='PNG'))

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class EventMenu(models.Model):
_inherit = "website.event.menu"
menu_type = fields.Selection(
selection_add=[('track', 'Event Tracks Menus'), ('track_proposal', 'Event Proposals Menus')],
ondelete={'track': 'cascade', 'track_proposal': 'cascade'})

30
models/website_menu.py Normal file
View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class WebsiteMenu(models.Model):
_inherit = "website.menu"
def unlink(self):
""" Override to synchronize event configuration fields with menu deletion.
This should be cleaned in upcoming versions. """
event_updates = {}
website_event_menus = self.env['website.event.menu'].search([('menu_id', 'in', self.ids)])
for event_menu in website_event_menus:
to_update = event_updates.setdefault(event_menu.event_id, list())
# specifically check for /track in menu URL; to avoid unchecking track field when removing
# agenda page that has also menu_type='track'
if event_menu.menu_type == 'track' and '/track' in event_menu.menu_id.url:
to_update.append('website_track')
# call super that resumes the unlink of menus entries (including website event menus)
res = super(WebsiteMenu, self).unlink()
# update events
for event, to_update in event_updates.items():
if to_update:
event.write(dict((fname, False) for fname in to_update))
return res

62
models/website_visitor.py Normal file
View File

@ -0,0 +1,62 @@
# -*- 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
class WebsiteVisitor(models.Model):
_name = 'website.visitor'
_inherit = ['website.visitor']
event_track_visitor_ids = fields.One2many(
'event.track.visitor', 'visitor_id', string="Track Visitors",
groups='event.group_event_user')
event_track_wishlisted_ids = fields.Many2many(
'event.track', string="Wishlisted Tracks",
compute="_compute_event_track_wishlisted_ids", compute_sudo=True,
search="_search_event_track_wishlisted_ids",
groups="event.group_event_user")
event_track_wishlisted_count = fields.Integer(
string="# Wishlisted",
compute="_compute_event_track_wishlisted_ids", compute_sudo=True,
groups='event.group_event_user')
@api.depends('event_track_visitor_ids.track_id', 'event_track_visitor_ids.is_wishlisted')
def _compute_event_track_wishlisted_ids(self):
results = self.env['event.track.visitor']._read_group(
[('visitor_id', 'in', self.ids), ('is_wishlisted', '=', True)],
['visitor_id'],
['track_id:array_agg'],
)
track_ids_map = {visitor.id: track_ids for visitor, track_ids in results}
for visitor in self:
visitor.event_track_wishlisted_ids = track_ids_map.get(visitor.id, [])
visitor.event_track_wishlisted_count = len(visitor.event_track_wishlisted_ids)
def _search_event_track_wishlisted_ids(self, operator, operand):
""" Search visitors with terms on wishlisted tracks. E.g. [('event_track_wishlisted_ids',
'in', [1, 2])] should return visitors having wishlisted tracks 1, 2. """
if operator == "not in":
raise NotImplementedError("Unsupported 'Not In' operation on track wishlist visitors")
track_visitors = self.env['event.track.visitor'].sudo().search([
('track_id', operator, operand),
('is_wishlisted', '=', True)
])
return [('id', 'in', track_visitors.visitor_id.ids)]
def _inactive_visitors_domain(self):
""" Visitors registered to push subscriptions are considered always active and should not be
deleted. """
domain = super()._inactive_visitors_domain()
return expression.AND([domain, [('event_track_visitor_ids', '=', False)]])
def _merge_visitor(self, target):
""" Override linking process to link wishlist to the final visitor. """
self.event_track_visitor_ids.visitor_id = target.id
track_visitor_wo_partner = self.event_track_visitor_ids.filtered(lambda track_visitor: not track_visitor.partner_id)
if track_visitor_wo_partner:
track_visitor_wo_partner.partner_id = target.partner_id
return super()._merge_visitor(target)

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="event_track_public" model="ir.rule">
<field name="name">Event Tracks: public/portal: published</field>
<field name="model_id" ref="website_event_track.model_event_track"/>
<field name="domain_force">[('website_published', '=', True)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="ir_rule_event_track_tag_public" model="ir.rule">
<field name="name">Event Track Tag: public/portal: color = published</field>
<field name="model_id" ref="website_event_track.model_event_track_tag"/>
<field name="domain_force">['&amp;', ('color', '!=', False), ('color', '!=', 0)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
</odoo>

Some files were not shown because too many files have changed in this diff Show More