[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:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user