2 commits - pykolab/xml tests/functional wallace/module_invitationpolicy.py

Thomas Brüderli bruederli at kolabsys.com
Mon Jul 21 18:40:03 CEST 2014


 pykolab/xml/event.py                                       |   11 ++
 tests/functional/test_wallace/test_007_invitationpolicy.py |   52 +++++++++++++
 wallace/module_invitationpolicy.py                         |   44 +++++++----
 3 files changed, 90 insertions(+), 17 deletions(-)

New commits:
commit d1f4cc6e05eea2f7d3b9e0a92ab0a2c5d10e926f
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jul 9 20:24:51 2014 -0400

    Refine invitationpolicy module's code and behavior on re-scheduling and updates

diff --git a/tests/functional/test_wallace/test_007_invitationpolicy.py b/tests/functional/test_wallace/test_007_invitationpolicy.py
index b12b785..8dc3ff7 100644
--- a/tests/functional/test_wallace/test_007_invitationpolicy.py
+++ b/tests/functional/test_wallace/test_007_invitationpolicy.py
@@ -322,6 +322,35 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
 
         return event.get_uid()
 
+    def update_calendar_event(self, uid, start=None, summary=None, sequence=0, user=None):
+        if user is None:
+            user = self.john
+
+        event = self.check_user_calendar_event(user['kolabtargetfolder'], uid)
+        if event:
+            if start is not None:
+                event.set_start(start)
+            if summary is not None:
+                event.set_summary(summary)
+            if sequence is not None:
+                event.set_sequence(sequence)
+
+            imap = IMAP()
+            imap.connect()
+
+            mailbox = imap.folder_quote(user['kolabtargetfolder'])
+            imap.set_acl(mailbox, "cyrus-admin", "lrswipkxtecda")
+            imap.imap.m.select(mailbox)
+
+            return imap.imap.m.append(
+                mailbox,
+                None,
+                None,
+                event.to_message().as_string()
+            )
+
+        return False
+
     def check_message_received(self, subject, from_addr=None, mailbox=None):
         if mailbox is None:
             mailbox = self.john['mailbox']
@@ -641,3 +670,26 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
         jacks = self.check_user_calendar_event(self.jack['kolabtargetfolder'], uid)
         self.assertEqual(jacks.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted)
         self.assertEqual(jacks.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative)
+
+        # PART 2: create conflicting event in jack's calendar
+        new_start = datetime.datetime(2014,8,21, 6,0,0, tzinfo=pytz.timezone("Europe/Berlin"))
+        self.create_calendar_event(new_start, user=self.jack, attendees=[], summary="blocker")
+
+        # re-schedule initial event to new date
+        self.update_calendar_event(uid, start=new_start, sequence=1, user=self.john)
+        self.send_itip_update(self.jane['mail'], uid, new_start, summary="test (updated)", sequence=1)
+        self.send_itip_update(self.jack['mail'], uid, new_start, summary="test (updated)", sequence=1)
+
+        # wait for replies to be processed and propagated
+        time.sleep(10)
+        event = self.check_user_calendar_event(self.john['kolabtargetfolder'], uid)
+        self.assertIsInstance(event, pykolab.xml.Event)
+
+        # check updated event in organizer's calendar (jack didn't reply yet)
+        self.assertEqual(event.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted)
+        self.assertEqual(event.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative)
+
+        # check partstats in jack's calendar: jack's status should remain needs-action
+        jacks = self.check_user_calendar_event(self.jack['kolabtargetfolder'], uid)
+        self.assertEqual(jacks.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted)
+        self.assertEqual(jacks.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartNeedsAction)
diff --git a/wallace/module_invitationpolicy.py b/wallace/module_invitationpolicy.py
index b7f59de..1426e2b 100644
--- a/wallace/module_invitationpolicy.py
+++ b/wallace/module_invitationpolicy.py
@@ -23,7 +23,7 @@ import tempfile
 import time
 from urlparse import urlparse
 import urllib
-import md5
+import hashlib
 
 from email import message_from_string
 from email.parser import Parser
@@ -235,7 +235,7 @@ def execute(*args, **kw):
         return filepath
 
     # we're looking at the first itip event object
-    itip_event = itip_events[0];
+    itip_event = itip_events[0]
 
     # for replies, the organizer is the recipient
     if itip_event['method'] == 'REPLY':
@@ -327,6 +327,7 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
     save_event = not nonpart or not partstat == kolabformat.PartNeedsAction
     rsvp = receiving_attendee.get_rsvp()
     scheduling_required = rsvp or partstat == kolabformat.PartNeedsAction
+    respond_with = receiving_attendee.get_participant_status(True)
     condition_fulfilled = True
 
     # find existing event in user's calendar
@@ -335,7 +336,7 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
     # compare sequence number to determine a (re-)scheduling request
     if existing is not None:
         log.debug(_("Existing event: %r") % (existing), level=9)
-        scheduling_required = itip_event['sequence'] > 0 and itip_event['sequence'] >= existing.get_sequence()
+        scheduling_required = itip_event['sequence'] > 0 and itip_event['sequence'] > existing.get_sequence()
         save_event = True
 
     # if scheduling: check availability
@@ -347,8 +348,6 @@ 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 rsvp or scheduling_required:
         respond_with = None
         if policy & ACT_ACCEPT and condition_fulfilled:
             respond_with = 'TENTATIVE' if policy & COND_TENTATIVE else 'ACCEPTED'
@@ -361,12 +360,14 @@ def process_itip_request(itip_event, policy, recipient_email, sender_email, rece
             # TODO: delegate (but to whom?)
             return None
 
+    # if RSVP, send an iTip REPLY
+    if rsvp or scheduling_required:
+        # set attendee's CN from LDAP record if yet missing
+        if not receiving_attendee.get_name() and receiving_user.has_key('cn'):
+            receiving_attendee.set_name(receiving_user['cn'])
+
         # send iTip reply
         if respond_with is not None:
-            # set attendee's CN from LDAP record if yet missing
-            if not receiving_attendee.get_name() and receiving_user.has_key('cn'):
-                receiving_attendee.set_name(receiving_user['cn'])
-
             receiving_attendee.set_participant_status(respond_with)
             send_reply(recipient_email, itip_event, invitation_response_text(),
                 subject=_('"%(summary)s" has been %(status)s'))
@@ -516,8 +517,6 @@ def user_dn_from_email_address(email_address):
     else:
         log.debug(_("No user record(s) found for %r") % (email_address), level=9)
 
-    auth.disconnect()
-
     return user_dn
 
 
@@ -766,7 +765,7 @@ def remove_write_lock(key, update=True):
 
 
 def get_lock_key(user, uid):
-    return md5.new("%s/%s" % (user['mail'], uid)).hexdigest()
+    return hashlib.md5("%s/%s" % (user['mail'], uid)).hexdigest()
 
 
 def update_event(event, user_rec):
@@ -909,13 +908,26 @@ def propagate_changes_to_attendees_calendars(event):
     """
     for attendee in event.get_attendees():
         attendee_user_dn = user_dn_from_email_address(attendee.get_email())
-        if attendee_user_dn is not None:
-            log.debug(_("Update attendee copy of %r") % (attendee_user_dn), level=9)
-
+        if attendee_user_dn:
             attendee_user = auth.get_entry_attributes(None, attendee_user_dn, ['*'])
             attendee_event = find_existing_event(event.uid, attendee_user, True)  # does IMAP authenticate
             if attendee_event:
-                attendee_event.event.setAttendees(event.get_attendees())
+                try:
+                    attendee_entry = attendee_event.get_attendee_by_email(attendee_user['mail'])
+                except:
+                    attendee_entry = None
+
+                # copy all attendees from master event (covers additions and removals)
+                new_attendees = kolabformat.vectorattendee();
+                for a in event.get_attendees():
+                    # keep my own entry intact
+                    if attendee_entry is not None and attendee_entry.get_email() == a.get_email():
+                        new_attendees.append(attendee_entry)
+                    else:
+                        new_attendees.append(a)
+
+                attendee_event.event.setAttendees(new_attendees)
+
                 success = update_event(attendee_event, attendee_user)
                 log.debug(_("Updated %s's copy of %r: %r") % (attendee_user['mail'], event.uid, success), level=8)
 


commit 33fc8d4ba377a22e9c4c5a61e22fd7d1e217ffa8
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jul 9 19:23:52 2014 -0400

    Better date text for all-day events

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 8550714..98a91aa 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -302,11 +302,20 @@ class Event(object):
     def get_date_text(self, date_format='%Y-%m-%d', time_format='%H:%M %Z'):
         start = self.get_start()
         end = self.get_end()
-        if start.date() == end.date():
+        all_day = not hasattr(start, 'date')
+        start_date = start.date() if not all_day else start
+        end_date = end.date() if not all_day else end
+
+        if start_date == end_date:
             end_format = time_format
         else:
             end_format = date_format + " " + time_format
 
+        if all_day:
+            time_format = ''
+            if start_date == end_date:
+                return start.strftime(date_format)
+
         return "%s - %s" % (start.strftime(date_format + " " + time_format), end.strftime(end_format))
 
     def get_exception_dates(self):




More information about the commits mailing list