Начальное наполнение
This commit is contained in:
parent
e684fc28c5
commit
6c6c826d7b
38
README.md
38
README.md
@ -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
5
__init__.py
Normal 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
61
__manifest__.py
Normal 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
6
controllers/__init__.py
Normal 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
12
controllers/event.py
Normal 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
528
controllers/event_track.py
Normal 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
|
||||
]
|
67
controllers/webmanifest.py
Normal file
67
controllers/webmanifest.py
Normal 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
10
data/event_data.xml
Normal 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
13
data/event_demo.xml
Normal 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
41
data/event_track_data.xml
Normal 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
766
data/event_track_demo.xml
Normal 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 & 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: William’s 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>
|
74
data/event_track_demo_description.xml
Normal file
74
data/event_track_demo_description.xml
Normal 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 you’re 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&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>
|
33
data/event_track_location_demo.xml
Normal file
33
data/event_track_location_demo.xml
Normal 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>
|
49
data/event_track_tag_demo.xml
Normal file
49
data/event_track_tag_demo.xml
Normal 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>
|
15
data/event_track_visitor_demo.xml
Normal file
15
data/event_track_visitor_demo.xml
Normal 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>
|
25
data/mail_message_subtype_data.xml
Normal file
25
data/mail_message_subtype_data.xml
Normal 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>
|
31
data/mail_template_data.xml
Normal file
31
data/mail_template_data.xml
Normal 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
32
data/mail_templates.xml
Normal 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
2462
i18n/af.po
Normal file
File diff suppressed because it is too large
Load Diff
2630
i18n/ar.po
Normal file
2630
i18n/ar.po
Normal file
File diff suppressed because it is too large
Load Diff
2467
i18n/az.po
Normal file
2467
i18n/az.po
Normal file
File diff suppressed because it is too large
Load Diff
2545
i18n/bg.po
Normal file
2545
i18n/bg.po
Normal file
File diff suppressed because it is too large
Load Diff
2463
i18n/bs.po
Normal file
2463
i18n/bs.po
Normal file
File diff suppressed because it is too large
Load Diff
2654
i18n/ca.po
Normal file
2654
i18n/ca.po
Normal file
File diff suppressed because it is too large
Load Diff
2545
i18n/cs.po
Normal file
2545
i18n/cs.po
Normal file
File diff suppressed because it is too large
Load Diff
2579
i18n/da.po
Normal file
2579
i18n/da.po
Normal file
File diff suppressed because it is too large
Load Diff
2669
i18n/de.po
Normal file
2669
i18n/de.po
Normal file
File diff suppressed because it is too large
Load Diff
2467
i18n/el.po
Normal file
2467
i18n/el.po
Normal file
File diff suppressed because it is too large
Load Diff
2461
i18n/en_GB.po
Normal file
2461
i18n/en_GB.po
Normal file
File diff suppressed because it is too large
Load Diff
2666
i18n/es.po
Normal file
2666
i18n/es.po
Normal file
File diff suppressed because it is too large
Load Diff
2667
i18n/es_419.po
Normal file
2667
i18n/es_419.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_BO.po
Normal file
2460
i18n/es_BO.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_CL.po
Normal file
2460
i18n/es_CL.po
Normal file
File diff suppressed because it is too large
Load Diff
2462
i18n/es_CO.po
Normal file
2462
i18n/es_CO.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_CR.po
Normal file
2460
i18n/es_CR.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_DO.po
Normal file
2460
i18n/es_DO.po
Normal file
File diff suppressed because it is too large
Load Diff
2464
i18n/es_EC.po
Normal file
2464
i18n/es_EC.po
Normal file
File diff suppressed because it is too large
Load Diff
2461
i18n/es_PE.po
Normal file
2461
i18n/es_PE.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_PY.po
Normal file
2460
i18n/es_PY.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/es_VE.po
Normal file
2460
i18n/es_VE.po
Normal file
File diff suppressed because it is too large
Load Diff
2601
i18n/et.po
Normal file
2601
i18n/et.po
Normal file
File diff suppressed because it is too large
Load Diff
2461
i18n/eu.po
Normal file
2461
i18n/eu.po
Normal file
File diff suppressed because it is too large
Load Diff
2552
i18n/fa.po
Normal file
2552
i18n/fa.po
Normal file
File diff suppressed because it is too large
Load Diff
2659
i18n/fi.po
Normal file
2659
i18n/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
2669
i18n/fr.po
Normal file
2669
i18n/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/fr_BE.po
Normal file
2460
i18n/fr_BE.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/fr_CA.po
Normal file
2460
i18n/fr_CA.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/gl.po
Normal file
2460
i18n/gl.po
Normal file
File diff suppressed because it is too large
Load Diff
2466
i18n/gu.po
Normal file
2466
i18n/gu.po
Normal file
File diff suppressed because it is too large
Load Diff
2561
i18n/he.po
Normal file
2561
i18n/he.po
Normal file
File diff suppressed because it is too large
Load Diff
2482
i18n/hr.po
Normal file
2482
i18n/hr.po
Normal file
File diff suppressed because it is too large
Load Diff
2552
i18n/hu.po
Normal file
2552
i18n/hu.po
Normal file
File diff suppressed because it is too large
Load Diff
2654
i18n/id.po
Normal file
2654
i18n/id.po
Normal file
File diff suppressed because it is too large
Load Diff
2458
i18n/is.po
Normal file
2458
i18n/is.po
Normal file
File diff suppressed because it is too large
Load Diff
2656
i18n/it.po
Normal file
2656
i18n/it.po
Normal file
File diff suppressed because it is too large
Load Diff
2590
i18n/ja.po
Normal file
2590
i18n/ja.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/ka.po
Normal file
2460
i18n/ka.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/kab.po
Normal file
2460
i18n/kab.po
Normal file
File diff suppressed because it is too large
Load Diff
2463
i18n/km.po
Normal file
2463
i18n/km.po
Normal file
File diff suppressed because it is too large
Load Diff
2599
i18n/ko.po
Normal file
2599
i18n/ko.po
Normal file
File diff suppressed because it is too large
Load Diff
2458
i18n/lb.po
Normal file
2458
i18n/lb.po
Normal file
File diff suppressed because it is too large
Load Diff
2556
i18n/lt.po
Normal file
2556
i18n/lt.po
Normal file
File diff suppressed because it is too large
Load Diff
2540
i18n/lv.po
Normal file
2540
i18n/lv.po
Normal file
File diff suppressed because it is too large
Load Diff
2465
i18n/mk.po
Normal file
2465
i18n/mk.po
Normal file
File diff suppressed because it is too large
Load Diff
2490
i18n/mn.po
Normal file
2490
i18n/mn.po
Normal file
File diff suppressed because it is too large
Load Diff
2471
i18n/nb.po
Normal file
2471
i18n/nb.po
Normal file
File diff suppressed because it is too large
Load Diff
2655
i18n/nl.po
Normal file
2655
i18n/nl.po
Normal file
File diff suppressed because it is too large
Load Diff
2644
i18n/pl.po
Normal file
2644
i18n/pl.po
Normal file
File diff suppressed because it is too large
Load Diff
2541
i18n/pt.po
Normal file
2541
i18n/pt.po
Normal file
File diff suppressed because it is too large
Load Diff
2645
i18n/pt_BR.po
Normal file
2645
i18n/pt_BR.po
Normal file
File diff suppressed because it is too large
Load Diff
2475
i18n/ro.po
Normal file
2475
i18n/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
2668
i18n/ru.po
Normal file
2668
i18n/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
2542
i18n/sk.po
Normal file
2542
i18n/sk.po
Normal file
File diff suppressed because it is too large
Load Diff
2549
i18n/sl.po
Normal file
2549
i18n/sl.po
Normal file
File diff suppressed because it is too large
Load Diff
2460
i18n/sq.po
Normal file
2460
i18n/sq.po
Normal file
File diff suppressed because it is too large
Load Diff
2637
i18n/sr.po
Normal file
2637
i18n/sr.po
Normal file
File diff suppressed because it is too large
Load Diff
2464
i18n/sr@latin.po
Normal file
2464
i18n/sr@latin.po
Normal file
File diff suppressed because it is too large
Load Diff
2551
i18n/sv.po
Normal file
2551
i18n/sv.po
Normal file
File diff suppressed because it is too large
Load Diff
2627
i18n/th.po
Normal file
2627
i18n/th.po
Normal file
File diff suppressed because it is too large
Load Diff
2646
i18n/tr.po
Normal file
2646
i18n/tr.po
Normal file
File diff suppressed because it is too large
Load Diff
2645
i18n/uk.po
Normal file
2645
i18n/uk.po
Normal file
File diff suppressed because it is too large
Load Diff
2606
i18n/vi.po
Normal file
2606
i18n/vi.po
Normal file
File diff suppressed because it is too large
Load Diff
2522
i18n/website_event_track.pot
Normal file
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
2595
i18n/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
2590
i18n/zh_TW.po
Normal file
2590
i18n/zh_TW.po
Normal file
File diff suppressed because it is too large
Load Diff
16
models/__init__.py
Normal file
16
models/__init__.py
Normal 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
91
models/event_event.py
Normal 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
612
models/event_track.py
Normal 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]
|
13
models/event_track_location.py
Normal file
13
models/event_track_location.py
Normal 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')
|
48
models/event_track_stage.py
Normal file
48
models/event_track_stage.py
Normal 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
27
models/event_track_tag.py
Normal 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!"),
|
||||
]
|
14
models/event_track_tag_category.py
Normal file
14
models/event_track_tag_category.py
Normal 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")
|
32
models/event_track_visitor.py
Normal file
32
models/event_track_visitor.py
Normal 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
22
models/event_type.py
Normal 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
|
10
models/res_config_settings.py
Normal file
10
models/res_config_settings.py
Normal 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
59
models/website.py
Normal 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'))
|
12
models/website_event_menu.py
Normal file
12
models/website_event_menu.py
Normal 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
30
models/website_menu.py
Normal 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
62
models/website_visitor.py
Normal 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)
|
26
security/event_track_security.xml
Normal file
26
security/event_track_security.xml
Normal 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">['&', ('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
Loading…
x
Reference in New Issue
Block a user