summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pykolab/xml/event.py6
-rw-r--r--tests/functional/test_wallace/test_007_invitationpolicy.py35
-rw-r--r--wallace/module_invitationpolicy.py41
3 files changed, 68 insertions, 14 deletions
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index ea84cc4..e438343 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -433,6 +433,9 @@ class Event(object):
def get_sequence(self):
return self.event.sequence()
+ def get_transparency(self):
+ return self.event.transparency()
+
def set_attendee_participant_status(self, attendee, status):
"""
Set the participant status of an attendee to status.
@@ -664,6 +667,9 @@ class Event(object):
def set_uid(self, uid):
self.event.setUid(str(uid))
+ def set_transparency(self, transp):
+ return self.event.setTransparency(transp)
+
def __str__(self):
event_xml = kolabformat.writeEvent(self.event)
diff --git a/tests/functional/test_wallace/test_007_invitationpolicy.py b/tests/functional/test_wallace/test_007_invitationpolicy.py
index 0490ec1..10a377f 100644
--- a/tests/functional/test_wallace/test_007_invitationpolicy.py
+++ b/tests/functional/test_wallace/test_007_invitationpolicy.py
@@ -159,7 +159,7 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
'dn': 'uid=manager,ou=People,dc=example,dc=org',
'mailbox': 'user/jane.manager@example.org',
'kolabtargetfolder': 'user/jane.manager/Calendar@example.org',
- 'kolabinvitationpolicy': ['ACT_ACCEPT_IF_NO_CONFLICT','ACT_REJECT_IF_CONFLICT']
+ 'kolabinvitationpolicy': ['ACT_ACCEPT_IF_NO_CONFLICT','ACT_REJECT_IF_CONFLICT', 'ACT_UPDATE']
}
from tests.functional.user_add import user_add
@@ -240,16 +240,19 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
},
mailto,
attendee_email,
- method="REPLY")
+ method='REPLY')
return uid
- def send_itip_cancel(self, resource_email, uid):
- self.send_message(itip_cancellation % (
- uid,
- resource_email
- ),
- resource_email)
+ def send_itip_cancel(self, attendee_email, uid, summary="test", sequence=1):
+ self.send_message(itip_cancellation % {
+ 'uid': uid,
+ 'mailto': attendee_email,
+ 'summary': summary,
+ 'sequence': sequence,
+ },
+ attendee_email,
+ method='CANCEL')
return uid
@@ -446,4 +449,20 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
attendee = event.get_attendee(self.jane['mail'])
self.assertIsInstance(attendee, pykolab.xml.Attendee)
self.assertEqual(attendee.get_participant_status(), kolabformat.PartAccepted)
+
+ def test_005_invitation_cancel(self):
+ uid = self.send_itip_invitation(self.jane['mail'], summary="cancelled")
+
+ response = self.check_message_received('"cancelled" has been ACCEPTED', self.jane['mail'])
+ self.assertIsInstance(response, email.message.Message)
+
+ self.send_itip_cancel(self.jane['mail'], uid, summary="cancelled")
+
+ time.sleep(10)
+ event = self.check_user_calendar_event(self.jane['kolabtargetfolder'], uid)
+ self.assertIsInstance(event, pykolab.xml.Event)
+ self.assertEqual(event.get_summary(), "cancelled")
+ self.assertEqual(event.get_status(), 'CANCELLED')
+ self.assertTrue(event.get_transparency())
+
\ No newline at end of file
diff --git a/wallace/module_invitationpolicy.py b/wallace/module_invitationpolicy.py
index b5863c2..d4ed7d5 100644
--- a/wallace/module_invitationpolicy.py
+++ b/wallace/module_invitationpolicy.py
@@ -303,7 +303,8 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
nonpart = receiving_attendee.get_role() == kolabformat.NonParticipant
partstat = receiving_attendee.get_participant_status()
save_event = not nonpart or not partstat == kolabformat.PartNeedsAction
- scheduling_required = receiving_attendee.get_rsvp() or partstat == kolabformat.PartNeedsAction
+ rsvp = receiving_attendee.get_rsvp()
+ scheduling_required = rsvp or partstat == kolabformat.PartNeedsAction
condition_fulfilled = True
# find existing event in user's calendar
@@ -325,7 +326,7 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
log.debug(_("Precondition for event %r fulfilled: %r") % (itip_event['uid'], condition_fulfilled), level=5)
# if RSVP, send an iTip REPLY
- if scheduling_required:
+ if rsvp or scheduling_required:
respond_with = None
if policy & ACT_ACCEPT and condition_fulfilled:
respond_with = 'TENTATIVE' if policy & MOD_TENTATIVE else 'ACCEPTED'
@@ -348,6 +349,10 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
send_reply(recipient_email, itip_event, invitation_response_text(),
subject=_('"%(summary)s" has been %(status)s'))
+ # elif partstat == kolabformat.PartNeedsAction and conf.get('wallace','invitationpolicy_always_copy_to_calendar'):
+ # TODO: copy the invitation into the user's calendar with unchanged PARTSTAT
+ # TODO: or use ACT_POSTPONE for this?
+
else:
# policy doesn't match, pass on to next one
return None
@@ -407,8 +412,7 @@ def process_itip_reply(itip_event, policy, recipient_email, sender_email, receiv
return MESSAGE_FORWARD
# update the organizer's copy of the event
- delete_event(existing)
- if store_event(existing, receiving_user, existing._imap_folder):
+ if update_event(existing, receiving_user):
# TODO: send (consolidated) notification to organizer if policy & ACT_UPDATE_AND_NOTIFY:
# TODO: update all other attendee's copies if conf.get('wallace','invitationpolicy_autoupdate_other_attendees_on_reply'):
return MESSAGE_PROCESSED
@@ -430,9 +434,23 @@ def process_itip_cancel(itip_event, policy, recipient_email, sender_email, recei
log.info(_("Pass cancellation for manual processing"))
return MESSAGE_FORWARD
- # update_event_in_user_calendar(itip_event, receiving_user)
+ # auto-update the local copy with STATUS=CANCELLED
+ if policy & ACT_UPDATE:
+ # find existing event in user's calendar
+ existing = find_existing_event(itip_event, receiving_user)
+
+ if existing:
+ existing.set_status('CANCELLED')
+ existing.set_transparency(True)
+ if update_event(existing, receiving_user):
+ # TODO: send cancellation notification if policy & ACT_UPDATE_AND_NOTIFY: ?
+ return MESSAGE_PROCESSED
+
+ else:
+ log.error(_("The event referred by this reply was not found in the user's calendars. Forwarding to Inbox."))
+ return MESSAGE_FORWARD
- return MESSAGE_PROCESSED
+ return None
def user_dn_from_email_address(email_address):
@@ -659,6 +677,17 @@ def check_availability(itip_event, receiving_user):
return not conflict
+def update_event(event, user_rec):
+ """
+ Update the given event in IMAP (i.e. delete + append)
+ """
+ if hasattr(event, '_imap_folder'):
+ delete_event(event)
+ return store_event(event, user_rec, event._imap_folder)
+
+ return False
+
+
def store_event(event, user_rec, targetfolder=None):
"""
Append the given event object to the user's default calendar