# -*- coding: utf-8 -*- import datetime import urllib2 import vobject import rfc822 from zope import component from zope.event import notify from zope.lifecycleevent import ObjectAddedEvent, ObjectModifiedEvent from Products.CMFCore.WorkflowCore import WorkflowException from Products.CMFCore.utils import getToolByName from Products.Five.browser import BrowserView from plone.registry.interfaces import IRegistry from plone.app.textfield.value import RichTextValue from tabellio.config.interfaces import ITabellioSettings class IcalImport(BrowserView): def __call__(self): self.settings = component.getUtility(IRegistry).forInterface(ITabellioSettings, False) self.plone_utils = getToolByName(self.context, 'plone_utils') self.portal_workflow = getToolByName(self.context, 'portal_workflow') url = self.request.form.get('url') if self.settings.ical_username: password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() password_mgr.add_password(None, url, self.settings.ical_username, self.settings.ical_password) handler = urllib2.HTTPBasicAuthHandler(password_mgr) opener = urllib2.build_opener(handler) urlopen = opener.open else: urlopen = urllib2.urlopen content = urlopen(url).read() parsed_cal = vobject.readOne(content) seen_ids = [] created = 0 updated = 0 for vevent in parsed_cal.vevent_list: title = vevent.summary.value date_start = vevent.dtstart.value if isinstance(date_start, datetime.datetime): date_start = datetime.datetime(*date_start.timetuple()[:6]) elif isinstance(date_start, datetime.date): date_start = datetime.datetime.fromordinal(date_start.toordinal()) try: date_end = vevent.dtend.value if isinstance(date_end, datetime.datetime): date_end = datetime.datetime(*date_end.timetuple()[:6]) elif isinstance(date_end, datetime.date): date_end = datetime.datetime.fromordinal(date_end.toordinal()) except: date_end = None location = None try: location = vevent.location.value except: pass if location and '@' in location: try: location, email = rfc822.parseaddr(location) except: pass if location: # some special substitutions for PCF common locations location = location.replace(u'HL ', u'Hôtel de Ligne - ') location = location.replace(u'HG ', u'Hôtel du Greffe - ') base_id = '%(year)04d%(month)02d%(day)02d' base_parts = {'year': date_start.year, 'month': date_start.month, 'day': date_start.day} if date_start.hour != 0 and date_start.minute != 0: base_id = base_id + '-%(hour)02d%(minute)02d' base_parts.update({'hour': date_start.hour, 'minute': date_start.minute}) if location: base_id = base_id + '-%(location)s' base_parts.update({'location': location}) if title: base_id = base_id + '-%(title)s' base_parts.update({'title': title}) event_id = self.plone_utils.normalizeString(base_id % base_parts) if not hasattr(self.context, event_id): self.context.invokeFactory('tabellio.agenda.event', event_id, title=title) created += 1 else: updated += 1 event = getattr(self.context, event_id) event.title = title event.start = date_start event.end = date_end description = None try: description = vevent.description.value except: pass if description: event.text = RichTextValue(raw=description, mimeType='text/plain', outputMimeType='text/x-html-safe') description = None try: description = vevent.x_alt_desc.value except: pass if description and type(description) is unicode: # there's some bug in the transformation chain that will at # the wrong time convert the   and turn it into an invalid # utf-8 character, therefore we replace all of them right now, # as it's safe to do. description = description.replace(' ', u'\xa0') if description: if '*~*~*~*~*~*~*~*~*~*' in description: # zimbra lovely marker description = '' + \ description[description.index('*~*~*~*~*~*~*~*~*~*')+29:] event.text = RichTextValue(raw=description, mimeType='text/html', outputMimeType='text/x-html-safe') event.place = location notify(ObjectModifiedEvent(event)) try: self.portal_workflow.doActionFor(getattr(self.context, event_id), 'publish') except WorkflowException: pass seen_ids.append(event.id) deleted = 0 for event_id in self.context.objectIds(): if event_id in seen_ids: continue self.context.manage_delObjects(event_id) deleted += 1 return 'OK (created: %s, updated: %s, deleted: %s)' % (created, updated, deleted)