diff options
-rw-r--r-- | pykolab/itip/__init__.py | 16 | ||||
-rw-r--r-- | tests/functional/test_wallace/test_005_resource_invitation.py | 42 | ||||
-rw-r--r-- | tests/unit/test-011-itip.py | 3 | ||||
-rw-r--r-- | wallace/module_invitationpolicy.py | 1 | ||||
-rw-r--r-- | wallace/module_resources.py | 20 |
5 files changed, 61 insertions, 21 deletions
diff --git a/pykolab/itip/__init__.py b/pykolab/itip/__init__.py index a81015b..83c9fe1 100644 --- a/pykolab/itip/__init__.py +++ b/pykolab/itip/__init__.py @@ -184,10 +184,10 @@ def check_event_conflict(kolab_event, itip_event): _iv = _ix # iterate through all exceptions (non-recurring) - elif _is is None and not itip_event['xml'].is_recurring() and itip_event['xml'].has_exceptions() and len(itip_event['xml'].get_exceptions()) > _ii: - _ix = itip_event['xml'].get_exceptions()[_ii] - _is = to_dt(_ix.get_start()) - _ie = to_dt(_ix.get_end()) + elif _is is None and not itip_event['xml'].is_recurring() and len(itip_event['xml'].get_exceptions()) > _ii: + _iv = itip_event['xml'].get_exceptions()[_ii] + _is = to_dt(_iv.get_start()) + _ie = to_dt(_iv.get_end()) _ii += 1 _es = to_dt(kolab_event.get_next_occurence(_es)) if kolab_event.is_recurring() else None @@ -202,10 +202,10 @@ def check_event_conflict(kolab_event, itip_event): _ev = _ex # iterate through all exceptions (non-recurring) - elif _es is None and not kolab_event.is_recurring() and kolab_event.has_exceptions() and len(kolab_event.get_exceptions()) > _ei: - _ex = kolab_event.get_exceptions()[_ei] - _es = to_dt(_ex.get_start()) - _ee = to_dt(_ex.get_end()) + elif _es is None and not kolab_event.is_recurring() and len(kolab_event.get_exceptions()) > _ei: + _ev = kolab_event.get_exceptions()[_ei] + _es = to_dt(_ev.get_start()) + _ee = to_dt(_ev.get_end()) _ei += 1 return conflict diff --git a/tests/functional/test_wallace/test_005_resource_invitation.py b/tests/functional/test_wallace/test_005_resource_invitation.py index e7ac0f3..af84fa6 100644 --- a/tests/functional/test_wallace/test_005_resource_invitation.py +++ b/tests/functional/test_wallace/test_005_resource_invitation.py @@ -360,7 +360,7 @@ class TestResourceInvitation(unittest.TestCase): return found - def check_resource_calendar_event(self, mailbox, uid=None, instance=None): + def check_resource_calendar_event(self, mailbox, uid=None): imap = IMAP() imap.connect() @@ -383,7 +383,7 @@ class TestResourceInvitation(unittest.TestCase): continue found = event_from_message(event_message) - if found and (instance is None or found.is_recurring() or xmlutils.dates_equal(instance, found.get_recurrence_id())): + if found: break time.sleep(1) @@ -904,16 +904,34 @@ class TestResourceInvitation(unittest.TestCase): self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + start.strftime('%Y%m%dT%H%M%S'), str(accept)) - # the resource calendar now has two reservations stored - one = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid, start) + # the resource calendar now has two reservations stored in one object + one = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid) self.assertIsInstance(one, pykolab.xml.Event) self.assertIsInstance(one.get_recurrence_id(), datetime.datetime) self.assertEqual(one.get_start().hour, exstart.hour) - two = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid, nextstart) + two = one.get_instance(nextstart) self.assertIsInstance(two, pykolab.xml.Event) self.assertIsInstance(two.get_recurrence_id(), datetime.datetime) + self.purge_mailbox(self.john['mailbox']) + + # send rescheduling request to the 2nd instance + self.send_itip_update(self.boxter['mail'], uid, nextstart + datetime.timedelta(hours=2), sequence=2, instance=nextstart) + + accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + self.assertIsInstance(accept, email.message.Message) + self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + nextstart.strftime('%Y%m%dT%H%M%S'), str(accept)) + + event = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + self.assertEqual(len(event.get_exceptions()), 1) + + two = event.get_instance(nextstart) + self.assertIsInstance(two, pykolab.xml.Event) + self.assertEqual(two.get_sequence(), 2) + self.assertEqual(two.get_start().hour, 20) + def test_019_cancel_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) @@ -937,6 +955,20 @@ class TestResourceInvitation(unittest.TestCase): self.assertEqual(exception.get_status(True), 'CANCELLED') self.assertTrue(exception.get_transparency()) + self.purge_mailbox(self.john['mailbox']) + + # store a single occurrence with recurrence-id + start = datetime.datetime(2015,3,2, 18,30,0) + uid = self.send_itip_invitation(self.passat['mail'], start, instance=start) + + accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + self.assertIsInstance(accept, email.message.Message) + + self.send_itip_cancel(self.passat['mail'], uid, instance=start) + + time.sleep(5) # wait for IMAP to update + self.assertEqual(self.check_resource_calendar_event(self.passat['kolabtargetfolder'], uid), None) + def test_020_owner_confirmation_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) diff --git a/tests/unit/test-011-itip.py b/tests/unit/test-011-itip.py index 8179aa9..e47d314 100644 --- a/tests/unit/test-011-itip.py +++ b/tests/unit/test-011-itip.py @@ -501,6 +501,7 @@ class TestITip(unittest.TestCase): second.set_start(dtstart + datetime.timedelta(hours=1)) second.set_end(dtstart + datetime.timedelta(hours=2)) second.set_recurrence_id(dtstart) + second.set_transparency(True) itip_event['xml'].add_exception(second) self.assertEqual(len(itip_event['xml'].get_exceptions()), 1) @@ -514,7 +515,7 @@ class TestITip(unittest.TestCase): event.set_start(datetime.datetime(2012,7,15, 11,0,0, tzinfo=itip_event['start'].tzinfo)) event.set_end(datetime.datetime(2012,7,15, 11,30,0, tzinfo=itip_event['start'].tzinfo)) - self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") + self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") def test_003_send_reply(self): diff --git a/wallace/module_invitationpolicy.py b/wallace/module_invitationpolicy.py index 70bf627..d6503a0 100644 --- a/wallace/module_invitationpolicy.py +++ b/wallace/module_invitationpolicy.py @@ -826,7 +826,6 @@ def find_existing_object(uid, type, recurrence_id, user_rec, lock=False): # return master, even if instance is not found if not event and master.uid == uid: - log.debug("Instance not found, returning master" % (), level=8) return (event, master) if event is not None: diff --git a/wallace/module_resources.py b/wallace/module_resources.py index c1a684c..6742658 100644 --- a/wallace/module_resources.py +++ b/wallace/module_resources.py @@ -336,7 +336,7 @@ def execute(*args, **kw): log.debug(_("Cancellation for entire event %r: deleting") % (itip_event['uid']), level=8) delete_resource_event(itip_event['uid'], resources[resource], event._msguid) # just cancel one single occurrence: add exception with status=cancelled - elif master and master.is_recurring(): + elif master is not None: log.debug(_("Cancellation for a single occurrence %r of %r: updating...") % (itip_event['recurrence-id'], itip_event['uid']), level=8) event.set_status('CANCELLED') event.set_transparency(True) @@ -687,9 +687,9 @@ def read_resource_calendar(resource_rec, itip_events): for itip in itip_events: conflict = check_event_conflict(event, itip) - if event.get_uid() == itip['uid'] and (event.is_recurring() or itip['recurrence-id'] == event.get_recurrence_id()): + if event.get_uid() == itip['uid']: setattr(event, '_msguid', msguid) - if event.is_recurring(): + if event.is_recurring() or itip['recurrence-id']: resource_rec['existing_master'] = event else: resource_rec['existing_events'].append(event) @@ -740,21 +740,29 @@ def find_existing_event(uid, recurrence_id, resource_rec): event = event_from_message(message_from_string(data[0][1])) # find instance in a recurring series - if recurrence_id and event.is_recurring(): + if recurrence_id and (event.is_recurring() or event.has_exceptions()): master = event event = master.get_instance(recurrence_id) setattr(master, '_msguid', msguid) + # return master, even if instance is not found + if not event and master.uid == uid: + return (event, master) + # compare recurrence-id and skip to next message if not matching - elif recurrence_id and not event.is_recurring() and not xmlutils.dates_equal(recurrence_id, event.get_recurrence_id()): + elif recurrence_id and not xmlutils.dates_equal(recurrence_id, event.get_recurrence_id()): log.debug(_("Recurrence-ID not matching on message %s, skipping: %r != %r") % ( msguid, recurrence_id, event.get_recurrence_id() ), level=8) continue - setattr(event, '_msguid', msguid) + + if event is not None: + setattr(event, '_msguid', msguid) except Exception, e: log.error(_("Failed to parse event from message %s/%s: %r") % (mailbox, num, e)) + event = None + master = None continue if event and event.uid == uid: |