diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_event/controllers | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_event/controllers')
| -rw-r--r-- | addons/website_event/controllers/__init__.py | 4 | ||||
| -rw-r--r-- | addons/website_event/controllers/community.py | 13 | ||||
| -rw-r--r-- | addons/website_event/controllers/main.py | 408 |
3 files changed, 425 insertions, 0 deletions
diff --git a/addons/website_event/controllers/__init__.py b/addons/website_event/controllers/__init__.py new file mode 100644 index 00000000..88cf7794 --- /dev/null +++ b/addons/website_event/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import main +from . import community diff --git a/addons/website_event/controllers/community.py b/addons/website_event/controllers/community.py new file mode 100644 index 00000000..3034e946 --- /dev/null +++ b/addons/website_event/controllers/community.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http +from odoo.http import request + + +class EventCommunityController(http.Controller): + + @http.route('/event/<model("event.event"):event>/community', type="http", auth="public", website=True, sitemap=False) + def community(self, event, lang=None, **kwargs): + """ This skeleton route will be overriden in website_event_track_quiz, website_event_meet and website_event_meet_quiz. """ + return request.render('website.page_404') diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py new file mode 100644 index 00000000..493c0515 --- /dev/null +++ b/addons/website_event/controllers/main.py @@ -0,0 +1,408 @@ +# -*- coding: utf-8 -*- + +import collections +import babel.dates +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.relativedelta import relativedelta + +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 + + +class WebsiteEventController(http.Controller): + + def sitemap_event(env, rule, qs): + if not qs or qs.lower() in '/events': + yield {'loc': '/events'} + + @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'] + SudoEventType = request.env['event.type'].sudo() + + searches.setdefault('search', '') + searches.setdefault('date', 'all') + searches.setdefault('tags', '') + searches.setdefault('type', 'all') + searches.setdefault('country', 'all') + + website = request.website + today = fields.Datetime.today() + + def sdn(date): + return fields.Datetime.to_string(date.replace(hour=23, minute=59, second=59)) + + def sd(date): + return fields.Datetime.to_string(date) + + def get_month_filter_domain(filter_name, months_delta): + first_day_of_the_month = today.replace(day=1) + filter_string = _('This month') if months_delta == 0 \ + else format_date(request.env, value=today + relativedelta(months=months_delta), + date_format='LLLL', lang_code=get_lang(request.env).code).capitalize() + return [filter_name, filter_string, [ + ("date_end", ">=", sd(first_day_of_the_month + relativedelta(months=months_delta))), + ("date_begin", "<", sd(first_day_of_the_month + relativedelta(months=months_delta+1)))], + 0] + + dates = [ + ['all', _('Upcoming Events'), [("date_end", ">", sd(today))], 0], + ['today', _('Today'), [ + ("date_end", ">", sd(today)), + ("date_begin", "<", sdn(today))], + 0], + get_month_filter_domain('month', 0), + ['old', _('Past Events'), [ + ("date_end", "<", sd(today))], + 0], + ] + + # search domains + domain_search = {'website_specific': website.website_domain()} + + if searches['search']: + domain_search['search'] = [('name', 'ilike', searches['search'])] + + search_tags = self._extract_searched_event_tags(searches) + 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 = defaultdict(list) + for tag in search_tags: + grouped_tags[tag.category_id].append(tag) + domain_search['tags'] = [] + for group in grouped_tags: + domain_search['tags'] = expression.AND([domain_search['tags'], [('tag_ids', 'in', [tag.id for tag in grouped_tags[group]])]]) + + current_date = None + current_type = None + current_country = None + for date in dates: + if searches["date"] == date[0]: + domain_search["date"] = date[2] + if date[0] != 'all': + current_date = date[1] + + if searches["type"] != 'all': + current_type = SudoEventType.browse(int(searches['type'])) + domain_search["type"] = [("event_type_id", "=", int(searches["type"]))] + + if searches["country"] != 'all' and searches["country"] != 'online': + current_country = request.env['res.country'].browse(int(searches['country'])) + domain_search["country"] = ['|', ("country_id", "=", int(searches["country"])), ("country_id", "=", False)] + elif searches["country"] == 'online': + domain_search["country"] = [("country_id", "=", False)] + + def dom_without(without): + domain = [] + for key, search in domain_search.items(): + if key != without: + domain += search + return domain + + # count by domains without self search + for date in dates: + if date[0] != 'old': + date[3] = Event.search_count(dom_without('date') + date[2]) + + domain = dom_without('type') + + domain = dom_without('country') + countries = Event.read_group(domain, ["id", "country_id"], groupby="country_id", orderby="country_id") + countries.insert(0, { + 'country_id_count': sum([int(country['country_id_count']) for country in countries]), + 'country_id': ("all", _("All Countries")) + }) + + step = 12 # Number of events per page + event_count = Event.search_count(dom_without("none")) + pager = website.pager( + url="/event", + url_args=searches, + total=event_count, + page=page, + step=step, + scope=5) + + order = 'date_begin' + if searches.get('date', 'all') == 'old': + order = 'date_begin desc' + order = 'is_published desc, ' + order + events = Event.search(dom_without("none"), limit=step, offset=pager['offset'], order=order) + + keep = QueryURL('/event', **{key: value for key, value in searches.items() if (key == 'search' or value != 'all')}) + + values = { + 'current_date': current_date, + 'current_country': current_country, + '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([]), + 'countries': countries, + 'pager': pager, + 'searches': searches, + 'search_tags': search_tags, + 'keep': keep, + } + + if searches['date'] == 'old': + # the only way to display this content is to set date=old so it must be canonical + values['canonical_params'] = OrderedMultiDict([('date', 'old')]) + + return request.render("website_event.index", values) + + @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): + if not event.can_access_from_current_website(): + raise werkzeug.exceptions.NotFound() + + values = { + 'event': event, + } + + if '.' not in page: + page = 'website_event.%s' % page + + try: + # Every event page view should have its own SEO. + values['seo_object'] = request.website.get_template(page) + values['main_object'] = event + except ValueError: + # page not found + values['path'] = re.sub(r"^website_event\.", '', page) + values['from_template'] = 'website_event.default_page' # .strip('website_event.') + page = request.website.is_publisher() and 'website.page_404' or 'http_routing.404' + + return request.render(page, values) + + @http.route(['''/event/<model("event.event"):event>'''], type='http', auth="public", website=True, sitemap=True) + def event(self, event, **post): + if not event.can_access_from_current_website(): + raise werkzeug.exceptions.NotFound() + + if event.menu_id and event.menu_id.child_id: + target_url = event.menu_id.child_id[0].url + else: + target_url = '/event/%s/register' % str(event.id) + if post.get('enable_editor') == '1': + target_url += '?enable_editor=1' + return request.redirect(target_url) + + @http.route(['''/event/<model("event.event"):event>/register'''], type='http', auth="public", website=True, sitemap=False) + def event_register(self, event, **post): + if not event.can_access_from_current_website(): + raise werkzeug.exceptions.NotFound() + + values = self._prepare_event_register_values(event, **post) + return request.render("website_event.event_description_full", values) + + def _prepare_event_register_values(self, event, **post): + """Return the require values to render the template.""" + urls = event._get_event_resource_urls() + return { + 'event': event, + 'main_object': event, + 'range': range, + 'google_url': urls.get('google_url'), + '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). + + :return: list of order per ticket: [{ + 'id': if of ticket if any (0 if no ticket), + 'ticket': browse record of ticket if any (None if no ticket), + 'name': ticket name (or generic 'Registration' name if no ticket), + 'quantity': number of registrations for that ticket, + }, {...}] + """ + ticket_order = {} + for key, value in form_details.items(): + registration_items = key.split('nb_register-') + if len(registration_items) != 2: + continue + ticket_order[int(registration_items[1])] = int(value) + + ticket_dict = dict((ticket.id, ticket) for ticket in request.env['event.event.ticket'].search([ + ('id', 'in', [tid for tid in ticket_order.keys() if tid]), + ('event_id', '=', event.id) + ])) + + return [{ + 'id': tid if ticket_dict.get(tid) else 0, + 'ticket': ticket_dict.get(tid), + 'name': ticket_dict[tid]['name'] if ticket_dict.get(tid) else _('Registration'), + 'quantity': count, + } for tid, count in ticket_order.items() if count] + + @http.route(['/event/<model("event.event"):event>/registration/new'], type='json', auth="public", methods=['POST'], website=True) + def registration_new(self, event, **post): + if not event.can_access_from_current_website(): + raise werkzeug.exceptions.NotFound() + + tickets = self._process_tickets_form(event, post) + availability_check = True + if event.seats_limited: + ordered_seats = 0 + for ticket in tickets: + ordered_seats += ticket['quantity'] + if event.seats_available < ordered_seats: + availability_check = False + if not tickets: + return False + return request.env['ir.ui.view']._render_template("website_event.registration_attendee_details", {'tickets': tickets, 'event': event, 'availability_check': availability_check}) + + def _process_attendees_form(self, event, form_details): + """ Process data posted from the attendee details form. + + :param form_details: posted data from frontend registration form, like + {'1-name': 'r', '1-email': 'r@r.com', '1-phone': '', '1-event_ticket_id': '1'} + """ + allowed_fields = request.env['event.registration']._get_website_registration_allowed_fields() + registration_fields = {key: v for key, v in request.env['event.registration']._fields.items() if key in allowed_fields} + registrations = {} + global_values = {} + for key, value in form_details.items(): + counter, attr_name = key.split('-', 1) + field_name = attr_name.split('-')[0] + if field_name not in registration_fields: + continue + elif isinstance(registration_fields[field_name], (fields.Many2one, fields.Integer)): + value = int(value) or False # 0 is considered as a void many2one aka False + else: + value = value + + if counter == '0': + global_values[attr_name] = value + else: + registrations.setdefault(counter, dict())[attr_name] = value + for key, value in global_values.items(): + for registration in registrations.values(): + registration[key] = value + + return list(registrations.values()) + + def _create_attendees_from_registration_post(self, event, registration_data): + """ Also try to set a visitor (from request) and + a partner (if visitor linked to a user for example). Purpose is to gather + as much informations as possible, notably to ease future communications. + Also try to update visitor informations based on registration info. """ + visitor_sudo = request.env['website.visitor']._get_visitor_from_request(force_create=True) + visitor_sudo._update_visitor_last_visit() + visitor_values = {} + + registrations_to_create = [] + for registration_values in registration_data: + registration_values['event_id'] = event.id + if not registration_values.get('partner_id') and visitor_sudo.partner_id: + registration_values['partner_id'] = visitor_sudo.partner_id.id + elif not registration_values.get('partner_id'): + registration_values['partner_id'] = request.env.user.partner_id.id + + if visitor_sudo: + # registration may give a name to the visitor, yay + if registration_values.get('name') and not visitor_sudo.name and not visitor_values.get('name'): + visitor_values['name'] = registration_values['name'] + # update registration based on visitor + registration_values['visitor_id'] = visitor_sudo.id + + registrations_to_create.append(registration_values) + + if visitor_values: + visitor_sudo.write(visitor_values) + + return request.env['event.registration'].sudo().create(registrations_to_create) + + @http.route(['''/event/<model("event.event"):event>/registration/confirm'''], type='http', auth="public", methods=['POST'], website=True) + def registration_confirm(self, event, **post): + if not event.can_access_from_current_website(): + raise werkzeug.exceptions.NotFound() + + registrations = self._process_attendees_form(event, post) + attendees_sudo = self._create_attendees_from_registration_post(event, registrations) + + return request.render("website_event.registration_complete", + self._get_registration_confirm_values(event, attendees_sudo)) + + def _get_registration_confirm_values(self, event, attendees_sudo): + urls = event._get_event_resource_urls() + return { + 'attendees': attendees_sudo, + 'event': event, + 'google_url': urls.get('google_url'), + 'iCal_url': urls.get('iCal_url') + } + + def _extract_searched_event_tags(self, searches): + tags = request.env['event.tag'] + if searches.get('tags'): + try: + tag_ids = literal_eval(searches['tags']) + except: + pass + else: + # perform a search to filter on existing / valid tags implicitely + apply rules on color + tags = request.env['event.tag'].search([('id', 'in', tag_ids)]) + return tags |
