[MERGE] (website_)event: improve communication and frontend management

This merge improves event and website_event in various ways:

  * set default event_mail_ids on event.event (one registration, two
    reminders) as communication is considered as default behavior;
  * improve event creation flow on the frontend by adding fields;
  * improve customization on website_event;
  * remove name_get in event tags to avoid redundancy in tag category tree
    view and remove noise;
  * improve cover image of an event (Especially to improve the editor UX);
  * various display or testing related fixes;

Task-2488019

closes odoo/odoo#68901

Related: odoo/upgrade#2395
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2021-08-24 00:29:11 +02:00
committed by GitHub
14 changed files with 487 additions and 120 deletions
+30 -2
View File
@@ -24,6 +24,29 @@ class EventType(models.Model):
_description = 'Event Template'
_order = 'sequence, id'
def _default_event_mail_type_ids(self):
return [(0, 0,
{'notification_type': 'mail',
'interval_nbr': 0,
'interval_unit': 'now',
'interval_type': 'after_sub',
'template_ref': 'mail.template, %i' % self.env.ref('event.event_subscription').id,
}),
(0, 0,
{'notification_type': 'mail',
'interval_nbr': 1,
'interval_unit': 'hours',
'interval_type': 'before_event',
'template_ref': 'mail.template, %i' % self.env.ref('event.event_reminder').id,
}),
(0, 0,
{'notification_type': 'mail',
'interval_nbr': 3,
'interval_unit': 'days',
'interval_type': 'before_event',
'template_ref': 'mail.template, %i' % self.env.ref('event.event_reminder').id,
})]
name = fields.Char('Event Template', required=True, translate=True)
note = fields.Html(string='Note')
sequence = fields.Integer()
@@ -43,7 +66,9 @@ class EventType(models.Model):
default_timezone = fields.Selection(
_tz_get, string='Timezone', default=lambda self: self.env.user.tz or 'UTC')
# communication
event_type_mail_ids = fields.One2many('event.type.mail', 'event_type_id', string='Mail Schedule')
event_type_mail_ids = fields.One2many(
'event.type.mail', 'event_type_id', string='Mail Schedule',
default=_default_event_mail_type_ids)
# ticket reports
ticket_instructions = fields.Html('Ticket Instructions', translate=True,
help="This information will be printed on your tickets.")
@@ -69,6 +94,9 @@ class EventEvent(models.Model):
def _default_description(self):
return self.env['ir.ui.view']._render_template('event.event_default_descripton')
def _default_event_mail_ids(self):
return self.env['event.type']._default_event_mail_type_ids()
name = fields.Char(string='Event', translate=True, required=True)
note = fields.Html(string='Note', store=True, compute="_compute_note", readonly=False)
description = fields.Html(string='Description', translate=html_translate, sanitize_attributes=False, sanitize_form=False, default=_default_description)
@@ -371,7 +399,7 @@ class EventEvent(models.Model):
"""
for event in self:
if not event.event_type_id and not event.event_mail_ids:
event.event_mail_ids = False
event.event_mail_ids = self._default_event_mail_ids()
continue
# lines to keep: those with already sent emails or registrations
+1 -1
View File
@@ -235,7 +235,7 @@ class EventRegistration(models.Model):
default_model='event.registration',
default_res_id=self.id,
default_use_template=bool(template),
default_template_ref='mail.template,%i' % template.id,
default_template_id=template.id,
default_composition_mode='comment',
custom_layout="mail.mail_notification_light",
)
-3
View File
@@ -29,6 +29,3 @@ class EventTag(models.Model):
color = fields.Integer(
string='Color Index', default=lambda self: self._default_color(),
help='Tag color. No color means no display in kanban or front-end, to distinguish internal tags from public categorization tags.')
def name_get(self):
return [(tag.id, "%s: %s" % (tag.category_id.name, tag.name)) for tag in self]
+35 -5
View File
@@ -86,6 +86,7 @@ class TestEventData(TestEventCommon):
'name': 'Event Update Type',
'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)),
'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)),
'event_mail_ids': False,
})
self.assertEqual(event.date_tz, self.env.user.tz)
self.assertFalse(event.seats_limited)
@@ -148,13 +149,12 @@ class TestEventData(TestEventCommon):
# setup test records
event_type_default = self.env['event.type'].create({
'name': 'Type Default',
'auto_confirm': True
'auto_confirm': True,
'event_type_mail_ids': False,
})
event_type_mails = self.env['event.type'].create({
'name': 'Type Mails',
'auto_confirm': False
})
event_type_mails.write({
'auto_confirm': False,
'event_type_mail_ids': [
Command.clear(),
Command.create({
@@ -164,7 +164,7 @@ class TestEventData(TestEventCommon):
'interval_type': 'after_event',
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder'),
})
]
],
})
event = self.env['event.event'].create({
'name': 'Event',
@@ -307,6 +307,36 @@ class TestEventData(TestEventCommon):
set(['Registration Ticket'])
)
@users('user_eventmanager')
def test_event_mail_default_config(self):
event = self.env['event.event'].create({
'name': 'Event Update Type',
'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)),
'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)),
})
self.assertEqual(event.date_tz, self.env.user.tz)
self.assertFalse(event.seats_limited)
self.assertFalse(event.auto_confirm)
#Event Communications: when no event type, default configuration
self.assertEqual(len(event.event_mail_ids), 3)
self.assertEqual(event.event_mail_ids[0].interval_unit, 'now')
self.assertEqual(event.event_mail_ids[0].interval_type, 'after_sub')
self.assertEqual(event.event_mail_ids[0].template_ref, self.env.ref('event.event_subscription'))
self.assertEqual(event.event_mail_ids[1].interval_nbr, 1)
self.assertEqual(event.event_mail_ids[1].interval_unit, 'hours')
self.assertEqual(event.event_mail_ids[1].interval_type, 'before_event')
self.assertEqual(event.event_mail_ids[1].template_ref, self.env.ref('event.event_reminder'))
self.assertEqual(event.event_mail_ids[2].interval_nbr, 3)
self.assertEqual(event.event_mail_ids[2].interval_unit, 'days')
self.assertEqual(event.event_mail_ids[2].interval_type, 'before_event')
self.assertEqual(event.event_mail_ids[2].template_ref, self.env.ref('event.event_reminder'))
event.write({
'event_mail_ids': False
})
self.assertEqual(event.event_mail_ids, self.env['event.mail'])
def test_event_mail_filter_template_on_event(self):
"""Test that the mail template are filtered to show only those which are related to the event registration model.
@@ -29,6 +29,7 @@ class TestSMSSchedule(TestEventCommon, SMSCase):
cls.event_0.write({
'event_mail_ids': [
(5, 0),
(0, 0, { # right at subscription
'interval_unit': 'now',
'interval_type': 'after_sub',
+82 -50
View File
@@ -1,21 +1,21 @@
# -*- coding: utf-8 -*-
import collections
import babel.dates
import pytz
import re
import werkzeug
from werkzeug.datastructures import OrderedMultiDict
from werkzeug.exceptions import NotFound
from ast import literal_eval
from collections import defaultdict
from datetime import datetime, timedelta
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta
from werkzeug.datastructures import OrderedMultiDict
from werkzeug.exceptions import NotFound
from odoo import fields, http, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.website.controllers.main import QueryURL
from odoo.addons.event.controllers.main import EventController
from odoo.http import request
from odoo.osv import expression
from odoo.tools.misc import get_lang, format_date
@@ -27,6 +27,10 @@ class WebsiteEventController(http.Controller):
if not qs or qs.lower() in '/events':
yield {'loc': '/events'}
# ------------------------------------------------------------
# EVENT LIST
# ------------------------------------------------------------
@http.route(['/event', '/event/page/<int:page>', '/events', '/events/page/<int:page>'], type='http', auth="public", website=True, sitemap=sitemap_event)
def events(self, page=1, **searches):
Event = request.env['event.event']
@@ -152,7 +156,7 @@ class WebsiteEventController(http.Controller):
'current_type': current_type,
'event_ids': events, # event_ids used in website_event_track so we keep name as it is
'dates': dates,
'categories': request.env['event.tag.category'].search([]),
'categories': request.env['event.tag.category'].search([('is_published', '=', True)]),
'countries': countries,
'pager': pager,
'searches': searches,
@@ -167,6 +171,10 @@ class WebsiteEventController(http.Controller):
return request.render("website_event.index", values)
# ------------------------------------------------------------
# EVENT PAGE
# ------------------------------------------------------------
@http.route(['''/event/<model("event.event"):event>/page/<path:page>'''], type='http', auth="public", website=True, sitemap=False)
def event_page(self, event, page, **post):
values = {
@@ -214,51 +222,6 @@ class WebsiteEventController(http.Controller):
'iCal_url': urls.get('iCal_url'),
}
@http.route('/event/add_event', type='json', auth="user", methods=['POST'], website=True)
def add_event(self, event_name="New Event", **kwargs):
event = self._add_event(event_name, request.context)
return "/event/%s/register?enable_editor=1" % slug(event)
def _add_event(self, event_name=None, context=None, **kwargs):
if not event_name:
event_name = _("New Event")
date_begin = datetime.today() + timedelta(days=(14))
vals = {
'name': event_name,
'date_begin': fields.Date.to_string(date_begin),
'date_end': fields.Date.to_string((date_begin + timedelta(days=(1)))),
'seats_available': 1000,
'website_id': request.website.id,
}
return request.env['event.event'].with_context(context or {}).create(vals)
def get_formated_date(self, event):
start_date = fields.Datetime.from_string(event.date_begin).date()
end_date = fields.Datetime.from_string(event.date_end).date()
month = babel.dates.get_month_names('abbreviated', locale=get_lang(event.env).code)[start_date.month]
return ('%s %s%s') % (month, start_date.strftime("%e"), (end_date != start_date and ("-" + end_date.strftime("%e")) or ""))
@http.route('/event/get_country_event_list', type='json', auth='public', website=True)
def get_country_events(self, **post):
Event = request.env['event.event']
country_code = request.session['geoip'].get('country_code')
result = {'events': [], 'country': False}
events = None
domain = request.website.website_domain()
if country_code:
country = request.env['res.country'].search([('code', '=', country_code)], limit=1)
events = Event.search(domain + ['|', ('address_id', '=', None), ('country_id.code', '=', country_code), ('date_begin', '>=', '%s 00:00:00' % fields.Date.today())], order="date_begin")
if not events:
events = Event.search(domain + [('date_begin', '>=', '%s 00:00:00' % fields.Date.today())], order="date_begin")
for event in events:
if country_code and event.country_id.code == country_code:
result['country'] = country
result['events'].append({
"date": self.get_formated_date(event),
"event": event,
"url": event.website_url})
return request.env['ir.ui.view']._render_template("website_event.country_events_list", result)
def _process_tickets_form(self, event, form_details):
""" Process posted data about ticket order. Generic ticket are supported
for event without tickets (generic registration).
@@ -414,6 +377,75 @@ class WebsiteEventController(http.Controller):
'iCal_url': urls.get('iCal_url')
}
# ------------------------------------------------------------
# EDITOR (NEW EVENT)
# ------------------------------------------------------------
@http.route('/event/add_event', type='json', auth="user", methods=['POST'], website=True)
def add_event(self, name, event_start, event_end, address_values, **kwargs):
values = self._prepare_event_values(name, event_start, event_end, address_values)
event = request.env['event.event'].create(values)
return "/event/%s/register?enable_editor=1" % slug(event)
def _prepare_event_values(self, name, event_start, event_end, address_values=None):
"""
Return the values to create a new event.
event_start,event_date are datetimes in the user tz.
address_values is used to either choose an existing location or create one as we allow it in the frontend.
"""
date_begin = parse(event_start).astimezone(pytz.utc).replace(tzinfo=None)
date_end = parse(event_end).astimezone(pytz.utc).replace(tzinfo=None)
address_id = request.env['res.partner']
if address_values:
(address_pid, address_vals) = int(address_values[0]), address_values[1]
address_id = address_pid
if address_pid == 0:
address_id = request.env['res.partner'].create(address_vals).id
return {
'name': name,
'date_begin': date_begin,
'date_end': date_end,
'address_id': address_id,
'seats_available': 1000,
'website_id': request.website.id,
'event_ticket_ids': request.env['event.event.ticket'],
}
# ------------------------------------------------------------
# TOOLS (JSON)
# ------------------------------------------------------------
@http.route('/event/get_country_event_list', type='json', auth='public', website=True)
def get_country_events(self, **post):
Event = request.env['event.event']
country_code = request.session['geoip'].get('country_code')
result = {'events': [], 'country': False}
events = None
domain = request.website.website_domain()
if country_code:
country = request.env['res.country'].search([('code', '=', country_code)], limit=1)
events = Event.search(domain + ['|', ('address_id', '=', None), ('country_id.code', '=', country_code), ('date_begin', '>=', '%s 00:00:00' % fields.Date.today())], order="date_begin")
if not events:
events = Event.search(domain + [('date_begin', '>=', '%s 00:00:00' % fields.Date.today())], order="date_begin")
for event in events:
if country_code and event.country_id.code == country_code:
result['country'] = country
result['events'].append({
"date": self.get_formated_date(event),
"event": event,
"url": event.website_url})
return request.env['ir.ui.view']._render_template("website_event.country_events_list", result)
# ------------------------------------------------------------
# TOOLS (HELPERS)
# ------------------------------------------------------------
def get_formated_date(self, event):
start_date = fields.Datetime.from_string(event.date_begin).date()
end_date = fields.Datetime.from_string(event.date_end).date()
month = babel.dates.get_month_names('abbreviated', locale=get_lang(event.env).code)[start_date.month]
return ('%s %s%s') % (month, start_date.strftime("%e"), (end_date != start_date and ("-" + end_date.strftime("%e")) or ""))
def _extract_searched_event_tags(self, searches):
tags = request.env['event.tag']
if searches.get('tags'):
+5 -1
View File
@@ -24,7 +24,11 @@ class Event(models.Model):
def _default_cover_properties(self):
res = super()._default_cover_properties()
res['opacity'] = '0.4'
res.update({
'background-image': "url('/website_event/static/src/img/event_cover_4.jpg')",
'opacity': '0.4',
'resize_class': 'cover_auto'
})
return res
# description
@@ -2,11 +2,257 @@ odoo.define('website_event.editor', function (require) {
"use strict";
var core = require('web.core');
var wUtils = require('website.utils');
var Dialog = require('web.Dialog');
var time = require('web.time');
var WebsiteNewMenu = require('website.newMenu');
var _t = core._t;
var EventCreateDialog = Dialog.extend({
xmlDependencies: Dialog.prototype.xmlDependencies.concat(['/website_event/static/src/xml/event_create.xml']),
template: 'website_event.event_create_dialog',
events: _.extend({}, Dialog.prototype.events, {
'change select[name="event_location"]': '_onLocationChanged',
'click .input-group-append': '_onDatetimeClicked',
}),
cssLibs: [
'/web/static/lib/daterangepicker/daterangepicker.css',
],
jsLibs: [
'/web/static/lib/daterangepicker/daterangepicker.js',
'/web/static/src/legacy/js/libs/daterangepicker.js',
],
/**
* @override
* @param {Object} parent
* @param {Object} options
*/
init: function (parent, options) {
options = _.defaults(options || {}, {
title: _t("New Event"),
size: 'medium',
buttons: [
{
text: _t("Create"),
classes: 'btn-primary',
click: this._onClickCreate.bind(this),
},
{
text: _t("Discard"),
close: true
},
]
});
this.eventStart = moment();
this.eventEnd = moment().add(1, "d");
this._super(parent, options);
},
/**
* @override
*/
start: function () {
var self = this;
return this._super.apply(this, arguments).then(() => {
// stat / end datetimes widgets
self.$('input.daterange-input').each(function () {
self._initDateRangePicker($(this));
});
// address select2 configuration
self.$('input.o_wevent_js_address_id').select2(self._getSelect2AddressConfig());
});
},
/**
* @param {*} errors
*/
_display_errors: function (errors) {
this.$("p.text-danger").toggleClass('d-none', true);
for (let i = 0; i < errors.length; i++) {
this.$("#" + errors[i] + " p.text-danger").toggleClass('d-none', false);
}
},
_getSelect2AddressConfig: function () {
var self = this;
return {
allowClear: true,
formatNoMatches: false,
multiple: false,
selection_data: false,
width: '100%',
createSearchChoice: function (name, data) {
var addedPartner = $(this.opts.element).select2('data');
if (_.filter(_.union(addedPartner, data), function (partner) {
return partner.text.toLowerCase().localeCompare(name.toLowerCase()) === 0;
}).length === 0) {
return {
create: true,
id: _.uniqueId('address_'),
name: name,
text: _.str.sprintf(_t('Create "%s"'), name),
};
}
},
fill_data: function (query, data) {
var that = this;
var partners = {results: []};
_.each(data, function (partner) {
var contact_address = partner.contact_address.replace(/[\n]+/g, '').trim();
if (contact_address && that.matcher(query.term, contact_address)) {
partners.results.push({
id: partner.id,
text: contact_address,
});
}
});
query.callback(partners);
},
formatSelection: function (data) {
if (data.name) {
data.text = data.name;
}
return data.text;
},
query: function (query) {
var that = this;
// fetch data only once and store it
if (!this.selection_data) {
self._rpc({
model: 'res.partner',
method: 'search_read',
fields: ['contact_address'],
domain: [],
}).then(function (data) {
that.fill_data(query, data);
that.selection_data = data;
});
} else {
this.fill_data(query, this.selection_data);
}
}
}
},
_getSelect2AddressValues: function () {
var select2Value = this.$('input.o_wevent_js_address_id').select2('data');
if (select2Value) {
if (select2Value.create) {
return [0, {'name': select2Value.name}];
} else {
return [select2Value.id, {}];
}
}
return [];
},
/**
*
* @param {*} $dateGroup
*/
_initDateRangePicker: function ($dateGroup) {
var minDate = moment().subtract(1, "d");
var maxDate = moment().add(200, "y");
var self = this;
var textDirection = _t.database.parameters.direction;
$dateGroup.daterangepicker({
// dates
endDate: this.eventEnd,
maxDate: maxDate,
minDate: minDate,
startDate: this.eventStart,
// display
locale: {
direction: textDirection,
format: time.getLangDatetimeFormat().replace(':ss', ''),
separator: ' 🠖 ',
applyLabel: _t('Apply'),
cancelLabel: _t('Cancel'),
weekLabel: 'W',
customRangeLabel: _t('Custom Range'),
daysOfWeek: moment.weekdaysMin(),
monthNames: moment.monthsShort(),
firstDay: moment.localeData().firstDayOfWeek()
},
opens: 'left',
timePicker: true,
timePicker24Hour: true,
viewDate: moment(new Date()).hours(minDate.hours()).minutes(minDate.minutes()).seconds(minDate.seconds()).milliseconds(minDate.milliseconds()),
}, function (start, end, label) {
self.eventStart = start;
self.eventEnd = end;
});
},
/**
* @private
*/
_prepareFormValues: function () {
return {
address_values: this._getSelect2AddressValues(),
event_start: this.eventStart,
event_end: this.eventEnd,
location: this.$('select[name=event_location]').val(),
name: this.$('input[name=name]').val().trim(),
};
},
/**
* @private
* @param {*} values
*/
_validateForm: function (values) {
var errors = [];
if (!values.name){
errors.push('name');
}
if (values.location === 'on_site' && !values.address_values.length) {
errors.push('address');
}
if (!values.event_start || !values.event_end) {
errors.push('event_dates');
}
return errors;
},
/**
* @private
*/
_submitForm: function () {
var self = this;
var eventValues = this._prepareFormValues();
var errors = this._validateForm(eventValues)
console.log(eventValues, errors);
if (errors.length) {
this._display_errors(errors);
return;
}
return this._rpc({
route: '/event/add_event',
params: eventValues,
}).then((url) => {
window.location.href = url;
return new Promise(function () {});
});
},
/**
* @private
*/
_onClickCreate: function () {
return this._submitForm();
},
/**
* @private
* @param {*} ev
*/
_onLocationChanged: function (ev) {
this.$('.show_visibility_address').toggleClass('d-none', ev.target.value === 'online');
},
});
WebsiteNewMenu.include({
actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
new_event: '_createNewEvent',
@@ -25,25 +271,12 @@ WebsiteNewMenu.include({
*/
_createNewEvent: function () {
var self = this;
return wUtils.prompt({
id: "editor_new_event",
window_title: _t("New Event"),
input: _t("Event Name"),
}).then(function (result) {
var eventName = result.val;
if (!eventName) {
return;
}
return self._rpc({
route: '/event/add_event',
params: {
event_name: eventName,
},
}).then(function (url) {
window.location.href = url;
return new Promise(function () {});
});
var def = new Promise(function (resolve) {
var dialog = new EventCreateDialog(self, {});
dialog.open();
dialog.on('closed', self, resolve);
});
return def;
},
});
});
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website_event.event_create_dialog">
<form id="editor_new_event" style="overflow:visible;">
<div class="form-group row" id="name">
<label for="name" class="col-md-2 col-form-label">Event Name</label>
<div class="col-md-10">
<input type="text" name="name" class="form-control" required="required"/>
</div>
<p class="text-danger mt-1 mb-0 col-md-8 d-none">Please fill in this field</p>
</div>
<div class="form-group row">
<label class="col-form-label col-md-2" for="event_location">Location</label>
<div class="col-md-10">
<select class="form-control" id="event_location" name="event_location">
<option value="online">Online</option>
<option value="on_site">On Site</option>
</select>
</div>
</div>
<div class="form-group row d-none show_visibility_address" id="address">
<label class="col-form-label col-md-2" for="address_id">Venue</label>
<div class="col-md-10">
<input type="text" class="form-control o_wevent_js_address_id" name="address_id" placeholder="Select Venue"/>
</div>
<p class="text-danger mt-1 mb-0 col-md-8 d-none">Please fill in this field</p>
</div>
<div class="form-group row d-flex justify-content-between align-items-center" id="event_dates">
<label class="col-form-label col-md-2" for="event_start_end">Start 🠖 End</label>
<div class="col-md-10 s_website_form_datetime input-group date o_wevent_form_datetime" data-target-input="nearest">
<input type="text" name="event_start_end" class="form-control daterange-input bg-transparent text-dark rounded-0 p-1"/>
</div>
<p class="text-danger mt-1 col-md-8 d-none">Please fill in this field</p>
</div>
</form>
</t>
</templates>
@@ -4,6 +4,7 @@ odoo.define("website_event.tour", function (require) {
const {_t} = require("web.core");
const {Markup} = require('web.utils');
var tour = require("web_tour.tour");
var time = require('web.time');
tour.register("website_event_tour", {
test: true,
@@ -17,11 +18,16 @@ odoo.define("website_event.tour", function (require) {
content: _t("Click here to create a new event."),
position: "bottom",
}, {
trigger: '.modal-dialog #editor_new_event input[type=text]',
trigger: '.modal-dialog #editor_new_event input[name=name]',
content: Markup(_t("Create a name for your new event and click <em>\"Continue\"</em>. e.g: Technical Training")),
run: 'text Technical Training',
position: "left",
}, {
trigger: '.modal-footer button.btn-primary.btn-continue',
trigger: '.modal-dialog #editor_new_event input[name=event_start_end]',
content: _t("Pick a Start date for your event"),
run: 'text ' + moment().format(time.getLangDatetimeFormat()) + ' - ' + moment().add(1, "d").format(time.getLangDatetimeFormat()),
}, {
trigger: '.modal-footer button.btn-primary',
extra_trigger: '#editor_new_event input[type=text][value!=""]',
content: Markup(_t("Click <em>Continue</em> to create the event.")),
position: "right",
@@ -17,7 +17,7 @@
<field name="model">event.tag.category</field>
<field name="inherit_id" ref="event.event_tag_category_view_tree"/>
<field name="arch" type="xml">
<field name="tag_ids" position="before">
<field name="tag_ids" position="after">
<field name="is_published" string="Show on Website"/>
</field>
</field>
@@ -47,7 +47,25 @@
<div class="container">
<div class="d-flex flex-column flex-sm-row justify-content-between w-100">
<span class="navbar-brand h4 my-0 mr-auto">Events</span>
<ul class="o_wevent_index_topbar_filters nav"/>
<ul class="o_wevent_index_topbar_filters nav">
<t t-foreach="categories" t-as="category">
<li t-if="category.is_published and category.tag_ids and any(tag.color for tag in category.tag_ids)" class="nav-item dropdown mr-2 my-1">
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-folder-open"/>
<t t-esc="category.name"/>
</a>
<div class="dropdown-menu">
<t t-foreach="category.tag_ids" t-as="tag">
<a t-if="tag.color"
t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
<t t-esc="tag.name"/>
</a>
</t>
</div>
</li>
</t>
</ul>
<div class="d-flex align-items-center flex-wrap pl-sm-3 pr-0">
<t t-call="website_event.events_search_box">
<t t-set="_searches" t-value="searches"/>
@@ -94,28 +112,6 @@
</xpath>
</template>
<template id="event_category_tag" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Tags">
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
<t t-foreach="categories" t-as="category">
<li t-if="category.is_published and category.tag_ids and any(tag.color for tag in category.tag_ids)" class="nav-item dropdown mr-2 my-1">
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-folder-open"/>
<t t-esc="category.name"/>
</a>
<div class="dropdown-menu">
<t t-foreach="category.tag_ids" t-as="tag">
<a t-if="tag.color"
t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
<t t-esc="tag.name"/>
</a>
</t>
</div>
</li>
</t>
</xpath>
</template>
<!-- Filter - Location -->
<template id="event_location" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Country">
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
@@ -320,7 +316,7 @@
</template>
<!-- Index - Sidebar - Snippet - Country Events -->
<template id="index_sidebar_country_event" inherit_id="website_event.index_sidebar" active="True" customize_show="True" name="Country Events" priority="70">
<template id="index_sidebar_country_event" inherit_id="website_event.index_sidebar" active="False" customize_show="True" name="Country Events" priority="70">
<xpath expr="//div[@id='o_wevent_index_sidebar']" position="inside">
<div class="o_wevent_sidebar_block">
<t t-snippet-call="website_event.s_country_events"/>
@@ -20,8 +20,8 @@
<i class="fa fa-folder-open text-primary mr-2"/><span t-field="event.event_type_id"/>
</a>
</li>
<li class="nav-item mr-3">
<a t-if="event.country_id" t-attf-href="/event?country=#{event.country_id.id}" class="nav-link">
<li t-if="event.country_id" class="nav-item mr-3">
<a t-attf-href="/event?country=#{event.country_id.id}" class="nav-link">
<i class="fa fa-map-marker text-primary mr-2"/><span t-field="event.country_id"/>
</a>
</li>
+12 -9
View File
@@ -55,14 +55,17 @@ class WebsiteEventSaleController(WebsiteEventController):
return res
def _add_event(self, event_name="New Event", context=None, **kwargs):
def _prepare_event_values(self, name, event_start, event_end, address_values=None):
values = super(WebsiteEventSaleController, self)._prepare_event_values(name, event_start, event_end, address_values)
product = request.env.ref('event_sale.product_product_event', raise_if_not_found=False)
if product:
context = dict(context or {}, default_event_ticket_ids=[[0, 0, {
'name': _('Registration'),
'product_id': product.id,
'end_sale_datetime': False,
'seats_max': 1000,
'price': 0,
}]])
return super(WebsiteEventSaleController, self)._add_event(event_name, context, **kwargs)
values.update({
'event_ticket_ids': [[0, 0, {
'name': _('Registration'),
'product_id': product.id,
'end_sale_datetime': False,
'seats_max': 1000,
'price': 0,
}]]
})
return values