From 7862b2b71cab2f2e86439fee9483b972a51783f6 Mon Sep 17 00:00:00 2001 From: Sanjay Jamod <sja@odoo.com> Date: Wed, 8 Nov 2017 17:44:35 +0530 Subject: [PATCH] [IMP] website_event: quick add to Outlook/Google Calendar In order for the user to easily add the date of the events to his calendar we need to facilitate the integration with Outlook/ Google Calendar. For this we add two buttons at the end of the subscription phase (one for Google Calendar and one for Outlook [ics file download]) Task ID: 39315 Closes #21854 --- addons/website_event/controllers/main.py | 18 +++++ addons/website_event/models/event.py | 65 +++++++++++++++++++ .../website_event/views/event_templates.xml | 8 +++ addons/website_event_sale/controllers/main.py | 3 + 4 files changed, 94 insertions(+) diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index b9c08b8232e2..33049caffaa1 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -3,6 +3,8 @@ import babel.dates import re import werkzeug +import json + from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta @@ -258,7 +260,23 @@ class WebsiteEventController(http.Controller): Attendees += Attendees.sudo().create( Attendees._prepare_attendee_values(registration)) + urls = event._get_event_resource_urls(Attendees.ids) return request.render("website_event.registration_complete", { 'attendees': Attendees, 'event': event, + 'google_url': urls.get('google_url'), + 'iCal_url': urls.get('iCal_url') }) + + @http.route(['/event/<model("event.event"):event>/ics/<string:name>.ics'], type='http', auth="public", website=True) + def make_event_ics_file(self, event, **kwargs): + if not event or not event.registration_ids: + return request.not_found() + attendee_ids = list(json.loads(kwargs.get('attendees', '{}').replace("'", '"')).values()) + files = event.get_ics_file(attendee_ids) + content = files[event.id] + return request.make_response(content, [ + ('Content-Type', 'application/octet-stream'), + ('Content-Length', len(content)), + ('Content-Disposition', 'attachment; filename=' + event.name + '.ics') + ]) diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index cf5c2bbd872c..573f8b34fbac 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -1,7 +1,26 @@ # -*- coding: utf-8 -*- +import logging +import pytz +import werkzeug + +from datetime import datetime + from odoo import api, fields, models, _ from odoo.addons.http_routing.models.ir_http import slug +from odoo.exceptions import UserError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT + +_logger = logging.getLogger(__name__) + +try: + import vobject +except ImportError: + _logger.warning("`vobject` Python module not found, iCal file generation disabled. Consider installing this module if you want to generate iCal files") + vobject = None + +GOOGLE_CALENDAR_URL = 'https://www.google.com/calendar/render?' + class EventType(models.Model): _name = 'event.type' @@ -115,3 +134,49 @@ class Event(models.Model): 'target': 'new', 'url': '/report/html/%s/%s?enable_editor' % ('event.event_event_report_template_badge', self.id), } + + @api.multi + def get_ics_file(self, attendee_ids): + """ Returns iCalendar file for the event invitation. + :returns a dict of .ics file content for each event + """ + result = {} + if not vobject: + return result + + for event in self: + cal = vobject.iCalendar() + cal_event = cal.add('vevent') + + if not event.date_begin or not event.date_end: + raise UserError(_("No date has been specified for the event, no file will be generated.")) + cal_event.add('created').value = fields.Datetime.from_string(fields.Datetime.now()).replace(tzinfo=pytz.timezone('UTC')) + cal_event.add('dtstart').value = fields.Datetime.from_string(event.date_begin).replace(tzinfo=pytz.timezone('UTC')) + cal_event.add('dtend').value = fields.Datetime.from_string(event.date_end).replace(tzinfo=pytz.timezone('UTC')) + cal_event.add('summary').value = event.name + if event.address_id: + cal_event.add('location').value = event.sudo().address_id.contact_address + + attendees = self.env['event.registration'].browse(attendee_ids) + for attendee in attendees: + attendee_add = cal_event.add('attendee') + attendee_add.value = u'MAILTO:' + (attendee.email or u'') + result[event.id] = cal.serialize().encode('utf-8') + return result + + def _get_event_resource_urls(self, attendees): + url_date_start = datetime.strptime(self.date_begin, DEFAULT_SERVER_DATETIME_FORMAT).strftime('%Y%m%dT%H%M%SZ') + url_date_stop = datetime.strptime(self.date_end, DEFAULT_SERVER_DATETIME_FORMAT).strftime('%Y%m%dT%H%M%SZ') + params = werkzeug.url_encode({ + 'action': 'TEMPLATE', + 'text': self.name, + 'dates': url_date_start + '/' + url_date_stop, + 'location': self.sudo().address_id.contact_address.replace('\n', ' '), + 'details': self.name, + }) + google_url = GOOGLE_CALENDAR_URL + params + params = werkzeug.url_encode({ + 'attendees': dict(('attendee_%s' % attendee, attendee) for attendee in attendees) + }) + iCal_url = '/event/%s/ics/%s.ics?' % (slug(self), self.name) + params + return {'google_url': google_url, 'iCal_url': iCal_url} diff --git a/addons/website_event/views/event_templates.xml b/addons/website_event/views/event_templates.xml index 9a04d4e7e57d..cf813068e972 100644 --- a/addons/website_event/views/event_templates.xml +++ b/addons/website_event/views/event_templates.xml @@ -594,6 +594,14 @@ <div class="col-md-9 mt16"> <h4><a t-attf-href="/event/#{slug(event)}"><t t-esc="event.name"/></a></h4> <i class="fa fa-clock-o"></i> <span itemprop="startDate" t-esc="event.date_begin_located"> </span> <i>to</i> <span itemprop="endDate" t-esc="event.date_end_located"> </span> + <div id="add_to_calendar" class="mt4"> + <a class="btn btn-primary mr8" t-att-href="iCal_url"> + <i class="fa fa-fw fa-arrow-right"/>Add to iCal/Outlook + </a> + <a class="btn btn-primary" t-att-href="google_url" target='_blank'> + <i class="fa fa-fw fa-arrow-right"/>Add to Google Calendar + </a> + </div> <div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-options='{ "widget": "contact", "fields": ["name", "address", "phone", "mobile", "email"] diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 7ccb7f9e4b53..25c32b88ccdd 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -42,9 +42,12 @@ class WebsiteEventSaleController(WebsiteEventController): attendees = request.env['event.registration'].browse(list(attendee_ids)) # clean context and session, then redirect to the confirmation page request.website.sale_reset() + urls = event._get_event_resource_urls(list(attendee_ids)) return request.render("website_event.registration_complete", { 'attendees': attendees, 'event': event, + 'google_url': urls.get('google_url'), + 'iCal_url': urls.get('iCal_url') }) return request.redirect("/shop/checkout") -- GitLab