This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
tabellio.icalimport/tabellio/icalimport/icalimport.py

141 lines
5.7 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2011-11-15 14:50:35 +01:00
import datetime
import urllib2
import vobject
import rfc822
2011-11-15 14:50:35 +01:00
from zope import component
2011-11-29 16:43:23 +01:00
from zope.event import notify
from zope.lifecycleevent import ObjectAddedEvent, ObjectModifiedEvent
2011-11-15 14:50:35 +01:00
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
2011-11-15 14:50:35 +01:00
class IcalImport(BrowserView):
def __call__(self):
self.settings = component.getUtility(IRegistry).forInterface(ITabellioSettings, False)
2011-11-15 14:50:35 +01:00
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()
2011-11-15 14:50:35 +01:00
parsed_cal = vobject.readOne(content)
2011-11-30 22:03:02 +01:00
seen_ids = []
created = 0
updated = 0
2011-11-15 14:50:35 +01:00
for vevent in parsed_cal.vevent_list:
title = vevent.summary.value
date_start = vevent.dtstart.value
2011-11-29 17:12:57 +01:00
if isinstance(date_start, datetime.datetime):
2011-11-29 17:18:02 +01:00
date_start = datetime.datetime(*date_start.timetuple()[:6])
2011-11-29 17:12:57 +01:00
elif isinstance(date_start, datetime.date):
2011-11-15 14:50:35 +01:00
date_start = datetime.datetime.fromordinal(date_start.toordinal())
try:
date_end = vevent.dtend.value
2011-11-29 17:12:57 +01:00
if isinstance(date_end, datetime.datetime):
2011-11-29 17:18:02 +01:00
date_end = datetime.datetime(*date_end.timetuple()[:6])
2011-11-29 17:12:57 +01:00
elif isinstance(date_end, datetime.date):
2011-11-15 14:50:35 +01:00
date_end = datetime.datetime.fromordinal(date_end.toordinal())
2011-11-15 16:09:36 +01:00
except:
2011-11-15 14:50:35 +01:00
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)
2011-11-15 14:50:35 +01:00
if not hasattr(self.context, event_id):
self.context.invokeFactory('tabellio.agenda.event', event_id, title=title)
2011-11-30 22:03:02 +01:00
created += 1
else:
updated += 1
2011-11-15 14:50:35 +01:00
event = getattr(self.context, event_id)
event.title = title
event.start = date_start
event.end = date_end
description = None
2011-11-15 14:50:35 +01:00
try:
description = vevent.description.value
2011-11-15 16:09:36 +01:00
except:
2011-11-15 14:50:35 +01:00
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
2011-11-30 21:48:29 +01:00
if description and type(description) is unicode:
# there's some bug in the transformation chain that will at
# the wrong time convert the &nbsp 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 = '<html><body>' + \
description[description.index('*~*~*~*~*~*~*~*~*~*')+29:]
event.text = RichTextValue(raw=description,
mimeType='text/html', outputMimeType='text/x-html-safe')
event.place = location
2011-11-29 16:43:23 +01:00
notify(ObjectModifiedEvent(event))
2011-11-15 14:50:35 +01:00
try:
self.portal_workflow.doActionFor(getattr(self.context, event_id), 'publish')
except WorkflowException:
pass
2011-11-30 22:03:02 +01:00
seen_ids.append(event.id)
2011-11-30 22:03:02 +01:00
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)