[CalendarServer-changes] [5292] CalendarServer/branches/users/cdaboo/shared-calendars-5187/ twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Fri Mar 12 11:42:29 PST 2010


Revision: 5292
          http://trac.macosforge.org/projects/calendarserver/changeset/5292
Author:   cdaboo at apple.com
Date:     2010-03-12 11:42:28 -0800 (Fri, 12 Mar 2010)
Log Message:
-----------
XML schema clean-up and reply notifications to sharer.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py	2010-03-12 19:39:52 UTC (rev 5291)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py	2010-03-12 19:42:28 UTC (rev 5292)
@@ -590,13 +590,20 @@
     namespace = calendarserver_namespace
     name = "read-write"
 
+class ReadWriteScheduleAccess (davxml.WebDAVEmptyElement):
+    """
+    Denotes read and write and schedule access on a shared calendar.
+    """
+    namespace = calendarserver_namespace
+    name = "read-write-schedule"
+
 class UID (davxml.WebDAVTextElement):
     namespace = calendarserver_namespace
     name = "uid"
 
-class Sequence (davxml.WebDAVTextElement):
+class InReplyTo (davxml.WebDAVTextElement):
     namespace = calendarserver_namespace
-    name = "sequence"
+    name = "in-reply-to"
 
 ##
 # Notifications
@@ -649,7 +656,8 @@
     name = "share"
 
     allowed_children = {
-        (calendarserver_namespace, "set" )   : (0, None),
+        (calendarserver_namespace, "set" )    : (0, None),
+        (calendarserver_namespace, "remove" ) : (0, None),
     }
 
 class InviteSet (davxml.WebDAVElement):
@@ -657,10 +665,11 @@
     name = "set"
 
     allowed_children = {
-        (calendarserver_namespace, "summary" )   : (0, 1),
-        (calendarserver_namespace, "attendee" )  : (1, 1),
-        (calendarserver_namespace, "read" )      : (0, 1),
-        (calendarserver_namespace, "read-write" ): (0, 1),
+        (dav_namespace,            "href" )                : (1, 1),
+        (calendarserver_namespace, "summary" )             : (0, 1),
+        (calendarserver_namespace, "read" )                : (0, 1),
+        (calendarserver_namespace, "read-write" )          : (0, 1),
+        (calendarserver_namespace, "read-write-schedule" ) : (0, 1),
     }
 
 class InviteRemove (davxml.WebDAVElement):
@@ -668,9 +677,10 @@
     name = "remove"
 
     allowed_children = {
-        (calendarserver_namespace, "attendee" )  : (1, 1),
-        (calendarserver_namespace, "read" )      : (0, 1),
-        (calendarserver_namespace, "read-write" ): (0, 1),
+        (dav_namespace,            "href" )                : (1, 1),
+        (calendarserver_namespace, "read" )                : (0, 1),
+        (calendarserver_namespace, "read-write" )          : (0, 1),
+        (calendarserver_namespace, "read-write-schedule" ) : (0, 1),
     }
 
 class InviteUser (davxml.WebDAVElement):
@@ -679,13 +689,13 @@
 
     allowed_children = {
         (calendarserver_namespace, "href" )              : (1, 1),
-        (calendarserver_namespace, "access" )            : (0, 1),
-        (calendarserver_namespace, "summary" )           : (0, 1),
         (calendarserver_namespace, "invite-noresponse" ) : (0, 1),
         (calendarserver_namespace, "invite-deleted" )    : (0, 1),
         (calendarserver_namespace, "invite-accepted" )   : (0, 1),
         (calendarserver_namespace, "invite-declined" )   : (0, 1),
         (calendarserver_namespace, "invite-invalid" )    : (0, 1),
+        (calendarserver_namespace, "access" )            : (1, 1),
+        (calendarserver_namespace, "summary" )           : (0, 1),
     }
 
 class InviteAccess (davxml.WebDAVElement):
@@ -693,8 +703,9 @@
     name = "access"
 
     allowed_children = {
-        (calendarserver_namespace, "read" )      : (0, 1),
-        (calendarserver_namespace, "read-write" ): (0, 1),
+        (calendarserver_namespace, "read" )               : (0, 1),
+        (calendarserver_namespace, "read-write" )         : (0, 1),
+        (calendarserver_namespace, "read-write-schedule" ): (0, 1),
     }
 
 class Invite (davxml.WebDAVElement):
@@ -763,6 +774,23 @@
         InviteSummary.qname()            : (0, 1),
     }
 
+    allowed_attributes = {
+        "shared-type" : True,
+    }
+
+class InviteReply (davxml.WebDAVElement):
+    namespace = calendarserver_namespace
+    name = "invite-reply"
+
+    allowed_children = {
+        (dav_namespace, "href")          : (0, 1),
+        InviteStatusAccepted.qname()     : (0, 1),
+        InviteStatusDeclined.qname()     : (0, 1),
+        HostURL.qname()                  : (0, 1),
+        InReplyTo.qname()                : (0, 1),
+        InviteSummary.qname()            : (0, 1),
+    }
+
 class ResourceUpdateAdded(davxml.WebDAVEmptyElement):
     namespace = calendarserver_namespace
     name = "resource-added-notification"
@@ -809,8 +837,8 @@
     allowed_children = {
         DTStamp.qname()                            : (0, None),
         UID.qname()                                : (0, None),
-        Sequence.qname()                           : (0, None),
         InviteNotification.qname()                 : (0, None),
+        InviteReply.qname()                        : (0, None),
         ResourceUpdateNotification.qname()         : (0, None),
         SharedCalendarUpdateNotification.qname()   : (0, None),
     }
@@ -826,7 +854,7 @@
 
     allowed_children = { (davxml.dav_namespace, "href"): (0, 1) }
 
-class NotificationType (davxml.WebDAVTextElement):
+class NotificationType (davxml.WebDAVElement):
     """
     A property to indicate what type of notification the resource represents.
     """
@@ -835,6 +863,10 @@
     hidden = True
     protected = True
 
+    allowed_children = {
+        InviteNotification.qname()                 : (0, None),
+        InviteReply.qname()                        : (0, None),
+    }
 
 ##
 # Extensions to davxml.ResourceType

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py	2010-03-12 19:39:52 UTC (rev 5291)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py	2010-03-12 19:42:28 UTC (rev 5292)
@@ -84,7 +84,7 @@
         yield self._writeNotification(request, uid, rname, xmltype, xmldata)
 
         # Update database
-        self.notificationsDB().addOrUpdateRecord(NotificationRecord(uid, rname, xmltype))
+        self.notificationsDB().addOrUpdateRecord(NotificationRecord(uid, rname, xmltype.name))
 
     def _writeNotification(self, request, uid, rname, xmltype, xmldata):
         raise NotImplementedError

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py	2010-03-12 19:39:52 UTC (rev 5291)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py	2010-03-12 19:42:28 UTC (rev 5292)
@@ -16,7 +16,6 @@
 
 from twext.python.log import LoggingMixIn
 from twext.web2 import responsecode
-from twext.web2.dav.element.base import PCDATAElement
 from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
 from twext.web2.dav.util import allDataFromStream
 from twext.web2.http import HTTPError, Response, StatusResponse
@@ -143,12 +142,12 @@
         Return the DAV:resourcetype stripped of any shared elements.
         """
         
-        rtype = self.resourceType()
-        newchildren = [child for child in rtype.children if child.qname() not in (
-            customxml.SharedOwner.qname(),
-        )] if rtype.children else ()
-        rtype.children = newchildren if newchildren else None
-        return rtype
+        if self.isCalendarCollection():
+            return "calendar"
+        elif self.isAddressBookCollection():
+            return "addressbook"
+        else:
+            return ""
 
     def validUserIDForShare(self, userid):
         """
@@ -252,11 +251,10 @@
         # Look for existing invite and update its fields or create new one
         record = self.invitesDB().recordForUserID(userid)
         if record:
-            record.sequence += 1
             record.access = inviteAccessMapFromXML[type(ace)]
             record.summary = summary
         else:
-            record = Invite(str(uuid4()), 1, userid, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
+            record = Invite(str(uuid4()), userid, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
         
         # Send invite
         yield self.sendInvite(record, request)
@@ -314,11 +312,11 @@
             pass
         
         # Generate invite XML
-        xmltype = customxml.InviteNotification.name
+        typeAttr = {'shared-type':self.sharedResourceType()}
+        xmltype = customxml.InviteNotification(**typeAttr)
         xmldata = customxml.Notification(
             customxml.DTStamp.fromString(dateTimeToString(datetime.datetime.now(tz=utc))),
             customxml.UID.fromString(record.inviteuid),
-            customxml.Sequence.fromString(str(record.sequence)),
             customxml.InviteNotification(
                 davxml.HRef.fromString(record.userid),
                 inviteStatusMapToXML[record.state](),
@@ -326,11 +324,11 @@
                 customxml.HostURL(
                     davxml.HRef.fromString(hosturl),
                 ),
-                self.sharedResourceType(),
                 customxml.Organizer(
                     davxml.HRef.fromString(owner),
                 ),
                 customxml.InviteSummary.fromString(record.summary),
+                **typeAttr
             ),
         ).toxml()
         
@@ -362,14 +360,10 @@
                 summary = None
                 for item in inviteset.children:
                     if isinstance(item, davxml.HRef):
-                        for attendeeItem in item.children:
-                            if isinstance(attendeeItem, PCDATAElement):
-                                userid = attendeeItem.data
+                        userid = str(item)
                         continue
                     if isinstance(item, customxml.InviteSummary):
-                        for summaryItem in item.children:
-                            if isinstance(summaryItem, PCDATAElement):
-                                summary = summaryItem.data
+                        summary = str(item)
                         continue
                     if isinstance(item, customxml.ReadAccess) or isinstance(item, customxml.ReadWriteAccess):
                         access = item
@@ -401,9 +395,7 @@
                 access = []
                 for item in inviteremove.children:
                     if isinstance(item, davxml.HRef):
-                        for attendeeItem in item.children:
-                            if isinstance(attendeeItem, PCDATAElement):
-                                userid = attendeeItem.data
+                        userid = str(item)
                         continue
                     if isinstance(item, customxml.ReadAccess) or isinstance(item, customxml.ReadWriteAccess):
                         access.append(item)
@@ -541,8 +533,8 @@
     set of shared calendfars.
     """
     
-    def acceptShare(self, request, hostUrl, inviteUID, displayname=None):
-        return self._changeShare(request, "ACCEPTED", hostUrl, inviteUID, displayname)
+    def acceptShare(self, request, hostUrl, replytoUID, displayname=None):
+        return self._changeShare(request, "ACCEPTED", hostUrl, replytoUID, displayname)
 
     def wouldAcceptShare(self, hostUrl, request):
         return succeed(True)
@@ -551,11 +543,11 @@
         """ Remove a shared calendar named in resourceName """
         return succeed(True)
 
-    def declineShare(self, request, hostUrl, inviteUID):
-        return self._changeShare(request, "DECLINED", hostUrl, inviteUID)
+    def declineShare(self, request, hostUrl, replytoUID):
+        return self._changeShare(request, "DECLINED", hostUrl, replytoUID)
 
     @inlineCallbacks
-    def _changeShare(self, request, state, hostUrl, inviteUID, displayname=None):
+    def _changeShare(self, request, state, hostUrl, replytoUID, displayname=None):
         """ Accept an invite to a shared calendar """
         
         # Change state in sharer invite
@@ -571,8 +563,41 @@
             ))
             
         # Change the record
-        yield sharedCalendar.changeUserInviteState(request, inviteUID, owner, state, displayname)
+        yield sharedCalendar.changeUserInviteState(request, replytoUID, owner, state, displayname)
 
+        yield self.sendReply(request, owner, sharedCalendar, state, hostUrl, replytoUID, displayname)
+
+    @inlineCallbacks
+    def sendReply(self, request, sharee, sharedCalendar, state, hostUrl, replytoUID, displayname=None):
+        
+
+        # Locate notifications collection for sharer
+        sharer = (yield sharedCalendar.ownerPrincipal(request))
+        notifications = (yield request.locateResource(sharer.notificationURL()))
+        
+        # Generate invite XML
+        notificationUID = "%s-reply" % (replytoUID,)
+        xmltype = customxml.InviteReply()
+        xmldata = customxml.Notification(
+            customxml.DTStamp.fromString(dateTimeToString(datetime.datetime.now(tz=utc))),
+            customxml.UID.fromString(notificationUID),
+            customxml.InviteReply(
+                *(
+                    (
+                        davxml.HRef.fromString(sharee),
+                        inviteStatusMapToXML[state](),
+                        customxml.HostURL(
+                            davxml.HRef.fromString(hostUrl),
+                        ),
+                        customxml.InReplyTo.fromString(replytoUID),
+                    ) + ((customxml.InviteSummary.fromString(displayname),) if displayname is not None else ())
+                )
+            ),
+        ).toxml()
+        
+        # Add to collections
+        yield notifications.addNotification(request, notificationUID, xmltype, xmldata)
+
     def xmlPOSTNoAuth(self, encoding, request):
         def _handleResponse(result):
             response = Response(code=responsecode.OK)
@@ -583,40 +608,36 @@
                 return error.value.response
             return Response(code=responsecode.BAD_REQUEST)
 
-        def _handleInviteUpdate(inviteupdatedoc):
+        def _handleInviteReply(invitereplydoc):
             """ Handle a user accepting or declining a sharing invite """
             hostUrl = None
             accepted = None
             summary = None
-            inviteUID = None
-            for item in inviteupdatedoc.children:
+            replytoUID = None
+            for item in invitereplydoc.children:
                 if isinstance(item, customxml.InviteStatusAccepted):
                     accepted = True
                 elif isinstance(item, customxml.InviteStatusDeclined):
                     accepted = False
                 elif isinstance(item, customxml.InviteSummary):
-                    for summaryitem in item.children:
-                        if isinstance(summaryitem, PCDATAElement):
-                            summary = summaryitem.data
+                    summary = str(item)
                 elif isinstance(item, customxml.HostURL):
                     for hosturlItem in item.children:
                         if isinstance(hosturlItem, davxml.HRef):
-                            for hrefItem in hosturlItem.children:
-                                if isinstance(hrefItem, PCDATAElement):
-                                    hostUrl = hrefItem.data
-                elif isinstance(item, customxml.UID):
-                    inviteUID = str(item)
+                            hostUrl = str(hosturlItem)
+                elif isinstance(item, customxml.InReplyTo):
+                    replytoUID = str(item)
             
-            if accepted is None or hostUrl is None or inviteUID is None:
+            if accepted is None or hostUrl is None or replytoUID is None:
                 raise HTTPError(ErrorResponse(
                     responsecode.FORBIDDEN,
                     (customxml.calendarserver_namespace, "valid-request"),
                     "missing required XML elements",
                 ))
             if accepted:
-                return self.acceptShare(request, hostUrl, inviteUID, displayname=summary)
+                return self.acceptShare(request, hostUrl, replytoUID, displayname=summary)
             else:
-                return self.declineShare(request, hostUrl, inviteUID)
+                return self.declineShare(request, hostUrl, replytoUID)
 
         def _getData(data):
             try:
@@ -627,7 +648,7 @@
 
             root = doc.root_element
             xmlDocHanders = {
-                customxml.InviteNotification: _handleInviteUpdate,          
+                customxml.InviteReply: _handleInviteReply,          
             }
             if type(root) in xmlDocHanders:
                 return xmlDocHanders[type(root)](root).addCallbacks(_handleResponse, errback=_handleErrorResponse)
@@ -637,8 +658,9 @@
         return allDataFromStream(request.stream).addCallback(_getData)
 
 inviteAccessMapToXML = {
-    "read-only"  : customxml.ReadAccess,
-    "read-write" : customxml.ReadWriteAccess,
+    "read-only"           : customxml.ReadAccess,
+    "read-write"          : customxml.ReadWriteAccess,
+    "read-write-schedule" : customxml.ReadWriteScheduleAccess,
 }
 inviteAccessMapFromXML = dict([(v,k) for k,v in inviteAccessMapToXML.iteritems()])
 
@@ -653,9 +675,8 @@
 
 class Invite(object):
     
-    def __init__(self, inviteuid, sequence, userid, access, state, summary):
+    def __init__(self, inviteuid, userid, access, state, summary):
         self.inviteuid = inviteuid
-        self.sequence = sequence
         self.userid = userid
         self.access = access
         self.state = state
@@ -708,9 +729,9 @@
     
     def addOrUpdateRecord(self, record):
 
-        self._db_execute("""insert or replace into INVITE (INVITEUID, SEQUENCE, USERID, ACCESS, STATE, SUMMARY)
-            values (:1, :2, :3, :4, :5, :6)
-            """, record.inviteuid, record.sequence, record.userid, record.access, record.state, record.summary,
+        self._db_execute("""insert or replace into INVITE (INVITEUID, USERID, ACCESS, STATE, SUMMARY)
+            values (:1, :2, :3, :4, :5)
+            """, record.inviteuid, record.userid, record.access, record.state, record.summary,
         )
     
     def removeRecordForUserID(self, userid):
@@ -746,7 +767,6 @@
         #
         # INVITE table is the primary table
         #   INVITEUID: UID for this invite
-        #   SEQUENCE: sequence number for this invite
         #   NAME: identifier of invitee
         #   ACCESS: Access mode for share
         #   STATE: Invite response status
@@ -756,7 +776,6 @@
             """
             create table INVITE (
                 INVITEUID      text unique,
-                SEQUENCE       integer,
                 USERID         text unique,
                 ACCESS         text,
                 STATE          text,

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py	2010-03-12 19:39:52 UTC (rev 5291)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py	2010-03-12 19:42:28 UTC (rev 5292)
@@ -1317,7 +1317,7 @@
         child = self.createSimilarFile(self.fp.child(rname).path)
         child.fp.setContent(xmldata)
         child.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"}))))
-        child.writeDeadProperty(customxml.NotificationType.fromString(xmltype))
+        child.writeDeadProperty(customxml.NotificationType(xmltype))
         
         return succeed(True)
 

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py	2010-03-12 19:39:52 UTC (rev 5291)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py	2010-03-12 19:42:28 UTC (rev 5292)
@@ -45,6 +45,7 @@
         
         self.resource.validUserIDForShare = self._fakeValidUserID
         self.resource.sendInvite = lambda record, request:succeed(True)
+        self.resource.removeInvite = lambda record, request:succeed(True)
         
     def _fakeValidUserID(self, userid):
         if userid.endswith("@example.com"):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100312/3046ddae/attachment-0001.html>


More information about the calendarserver-changes mailing list