[CalendarServer-changes] [853] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Mon Dec 18 18:50:03 PST 2006


Revision: 853
          http://trac.macosforge.org/projects/calendarserver/changeset/853
Author:   cdaboo at apple.com
Date:     2006-12-18 18:50:03 -0800 (Mon, 18 Dec 2006)

Log Message:
-----------
Re-organize notifications to follow the same scheme that dropbox resources now use. Also move the actual
notification action onto the dropbox collection since that is what actually generates it.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/calendar.py
    CalendarServer/trunk/twistedcaldav/dropbox.py
    CalendarServer/trunk/twistedcaldav/notifications.py
    CalendarServer/trunk/twistedcaldav/static.py

Modified: CalendarServer/trunk/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendar.py	2006-12-19 02:48:43 UTC (rev 852)
+++ CalendarServer/trunk/twistedcaldav/directory/calendar.py	2006-12-19 02:50:03 UTC (rev 853)
@@ -34,6 +34,7 @@
 from twistedcaldav.config import config
 from twistedcaldav.dropbox import DropBoxHomeResource
 from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource
+from twistedcaldav.notifications import NotificationsCollectionResource
 from twistedcaldav.resource import CalDAVResource
 from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
 from twistedcaldav.directory.idirectory import IDirectoryService
@@ -188,8 +189,11 @@
         if config.DropBoxEnabled:
             childlist += (
                 ("dropbox"      , DropBoxHomeResource      ),
-               #("notifications", NotificationsHomeResource),
             )
+        if config.NotificationsEnabled:
+            childlist += (
+                ("notifications", NotificationsCollectionResource),
+            )
         for name, cls in childlist:
             child = self.provisionChild(name)
             assert isinstance(child, cls), "Child %r is not a %s: %r" % (name, cls.__name__, child)

Modified: CalendarServer/trunk/twistedcaldav/dropbox.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/dropbox.py	2006-12-19 02:48:43 UTC (rev 852)
+++ CalendarServer/trunk/twistedcaldav/dropbox.py	2006-12-19 02:50:03 UTC (rev 853)
@@ -23,19 +23,25 @@
 
 __all__ = [
     "DropBoxHomeResource",
+    "DropBoxCollectionResource",
+    "DropBoxChildResource",
 ]
 
+import datetime
+import md5
+import time
+
 from twisted.internet.defer import deferredGenerator, waitForDeferred
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
 from twisted.web2.dav.http import HTTPError, ErrorResponse
-from twisted.web2.dav.resource import DAVResource, TwistedACLInheritable
+from twisted.web2.dav.resource import DAVResource, DAVPrincipalResource, TwistedACLInheritable
 from twisted.web2.dav.util import parentForURL
 
 from twistedcaldav import customxml
+from twistedcaldav.config import config
 from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.notifications import Notification
 
 class DropBoxHomeResource (DAVResource):
     """
@@ -80,25 +86,104 @@
         # Do inherited with possibly modified set of aces
         super(DropBoxCollectionResource, self).writeNewACEs(edited_aces)
 
+    def doNotification(self, request, myURI):
+        """
+        
+        @param childURI: URI of the child that changed and triggered the notification.
+        """
+        # First determine which principals should get notified
+        #
+        # Procedure:
+        #
+        # 1. Get the list of auto-subscribed principals from the parent collection property.
+        # 2. Expand any group principals in the list into their user principals.
+        # 3. Get the list of unsubscribed principals from the parent collection property.
+        # 4. Expand any group principals in the list into their user principals.
+        # 5. Generate a set from the difference between the subscribed list and unsubscribed list.
+        
+        def _expandPrincipals(principals):
+            result = []
+            for principal in principals:
+
+                principal = waitForDeferred(self.resolvePrincipal(principal.children[0], request))
+                yield principal
+                principal = principal.getResult()
+                if principal is None:
+                    continue
+        
+                presource = waitForDeferred(request.locateResource(str(principal)))
+                yield presource
+                presource = presource.getResult()
+        
+                if not isinstance(presource, DAVPrincipalResource):
+                    continue
+                
+                # Step 2. Expand groups.
+                members = presource.groupMembers()
+                
+                if members:
+                    for member in members:
+                        result.append(davxml.Principal(davxml.HRef.fromString(member)))
+                else:
+                    result.append(davxml.Principal(principal))
+            yield result
+
+        _expandPrincipals = deferredGenerator(_expandPrincipals)
+
+        # For drop box we look at the parent collection of the target resource and get the
+        # set of subscribed principals.
+        if not config.NotificationsEnabled or not self.hasDeadProperty(customxml.Subscribed):
+            yield None
+            return
+
+        principals = set()
+        autosubs = self.readDeadProperty(customxml.Subscribed).children
+        d = waitForDeferred(_expandPrincipals(autosubs))
+        yield d
+        autosubs = d.getResult()
+        principals.update(autosubs)
+        
+        for principal in principals:
+            if not isinstance(principal.children[0], davxml.HRef):
+                continue
+            purl = str(principal.children[0])
+            d = waitForDeferred(request.locateResource(purl))
+            yield d
+            presource = d.getResult()
+
+            collectionURL = presource.notificationsURL()
+            if collectionURL is None:
+                continue
+            d = waitForDeferred(request.locateResource(collectionURL))
+            yield d
+            collection = d.getResult()
+
+            name = "%s.xml" % (md5.new(str(self) + str(time.time()) + collectionURL).hexdigest(),)
+    
+            # Create new resource in the collection
+            d = waitForDeferred(request.locateChildResource(collection, name))    # This ensures the URI for the resource is mapped
+            yield d
+            child = d.getResult()
+
+            d = waitForDeferred(child.create(request, datetime.datetime.utcnow(), myURI))
+            yield d
+            d.getResult()
+        
+    doNotification = deferredGenerator(doNotification)
+
     def http_DELETE(self, request):
         #
-        # Handle notificiations
+        # Handle notification of this drop box collection being deleted
         #
-        parentURL=parentForURL(request.uri)
 
-        def gotParent(parent):
-            def gotResponse(response):
-                notification = Notification(parentURL=parentURL)
-                d = notification.doNotification(request, parent)
+        def gotResponse(response):
+            if response in (responsecode.OK, responsecode.NO_CONTENT):
+                d = self.doNotification(request, request.uri)
                 d.addCallback(lambda _: response)
-                return d
-
-            d = super(DropBoxCollectionResource, self).http_DELETE(request)
-            d.addCallback(gotResponse)
             return d
 
-        d = request.locateResource(parentURL)
-        d.addCallback(gotParent)
+        d = super(DropBoxCollectionResource, self).http_DELETE(request)
+        d.addCallback(gotResponse)
         return d
         
     def http_PUT(self, request):
@@ -190,8 +275,7 @@
         def gotParent(parent):
             def gotResponse(response):
                 if response.code in (responsecode.OK, responsecode.CREATED, responsecode.NO_CONTENT):
-                    notification = Notification(parentURL=parentForURL(request.uri))
-                    d = notification.doNotification(request, parent)
+                    d = parent.doNotification(request, parentURL)
                     d.addCallback(lambda _: response)
                     return d
 
@@ -202,3 +286,25 @@
         d = request.locateResource(parentURL)
         d.addCallback(gotParent)
         return d
+
+    def http_DELETE(self, request):
+        #
+        # Handle notificiations
+        #
+        parentURL=parentForURL(request.uri)
+
+        def gotParent(parent):
+            def gotResponse(response):
+                if response in (responsecode.OK, responsecode.NO_CONTENT):
+                    d = parent.doNotification(request, parentURL)
+                    d.addCallback(lambda _: response)
+                    return d
+
+            d = super(DropBoxChildResource, self).http_DELETE(request)
+            d.addCallback(gotResponse)
+            return d
+
+        d = request.locateResource(parentURL)
+        d.addCallback(gotParent)
+        return d
+        

Modified: CalendarServer/trunk/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/notifications.py	2006-12-19 02:48:43 UTC (rev 852)
+++ CalendarServer/trunk/twistedcaldav/notifications.py	2006-12-19 02:50:03 UTC (rev 853)
@@ -16,144 +16,53 @@
 # DRI: Cyrus Daboo, cdaboo at apple.com
 ##
 
-from twisted.internet.defer import deferredGenerator
-from twisted.internet.defer import waitForDeferred
-from twisted.web2.dav.method import put_common
-from twisted.web2.dav.resource import DAVPrincipalResource
-from twisted.web2.dav import davxml
 
-from twistedcaldav import customxml
-from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.extensions import DAVFile
-from twistedcaldav.extensions import DAVResource
-
-import datetime
-import md5
-import os
-import time
-
 """
 Implements collection change notification functionality. Any change to the contents of a collection will
 result in a notification resource deposited into subscriber's notifications collection.
 """
 
 __all__ = [
-    "Notification",
+    "NotificationCollectionResource",
     "NotificationResource",
-    "NotificationFile",
 ]
 
-class Notification(object):
-    """
-    Encapsulates a notification message.
-    """
-    
-    def __init__(self, parentURL):
-        self.timestamp = datetime.datetime.utcnow()
-        self.parentURL = parentURL
+from twisted.web2 import responsecode
+from twisted.web2.dav import davxml
+from twisted.web2.dav.http import ErrorResponse
 
-    def doNotification(self, request, parent):
-        """
-        Put the supplied notification into the notification collection of the specified principal.
-        
-        @param request: L{Request} for request in progress.
-        @param parent: L{DAVResource} for parent of resource trigerring the notification.
-        """
-        
-        # First determine which principals should get notified
-        #
-        # Procedure:
-        #
-        # 1. Get the list of auto-subscribed principals from the parent collection property.
-        # 2. Expand any group principals in the list into their user principals.
-        # 3. Get the list of unsubscribed principals from the parent collection property.
-        # 4. Expand any group principals in the list into their user principals.
-        # 5. Generate a set from the difference between the subscribed list and unsubscribed list.
-        
-        def _expandPrincipals(principals):
-            result = []
-            for principal in principals:
+from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.extensions import DAVResource
 
-                principal = waitForDeferred(parent.resolvePrincipal(principal.children[0], request))
-                yield principal
-                principal = principal.getResult()
-                if principal is None:
-                    continue
-        
-                presource = waitForDeferred(request.locateResource(str(principal)))
-                yield presource
-                presource = presource.getResult()
-        
-                if not isinstance(presource, DAVPrincipalResource):
-                    continue
-                
-                # Step 2. Expand groups.
-                members = presource.groupMembers()
-                
-                if members:
-                    for member in members:
-                        result.append(davxml.Principal(davxml.HRef.fromString(member)))
-                else:
-                    result.append(davxml.Principal(principal))
-            yield result
+class NotificationsCollectionResource (DAVResource):
+    def resourceType(self):
+        return davxml.ResourceType.notifications
 
-        _expandPrincipals = deferredGenerator(_expandPrincipals)
+    def isCollection(self):
+        return True
 
-        # For drop box we look at the parent collection of the target resource and get the
-        # set of subscribed principals.
-        if not parent.hasDeadProperty(customxml.Subscribed):
-            yield None
-            return
+    def notify(self):
+        # FIXME: Move doNotification() logic from above class to here
+        pass
 
-        principals = set()
-        autosubs = parent.readDeadProperty(customxml.Subscribed).children
-        d = waitForDeferred(_expandPrincipals(autosubs))
-        yield d
-        autosubs = d.getResult()
-        principals.update(autosubs)
-        
-        for principal in principals:
-            if not isinstance(principal.children[0], davxml.HRef):
-                continue
-            purl = str(principal.children[0])
-            d = waitForDeferred(request.locateResource(purl))
-            yield d
-            presource = d.getResult()
+    def http_PUT(self, request):
+        return ErrorResponse(
+            responsecode.FORBIDDEN,
+            (calendarserver_namespace, "notifications-collection-no-client-resources")
+        )
 
-            collectionURL = presource.notificationsURL()
-            if collectionURL is None:
-                continue
-            d = waitForDeferred(request.locateResource(collectionURL))
-            yield d
-            collection = d.getResult()
+    def http_MKCOL (self, request):
+        return ErrorResponse(
+            responsecode.FORBIDDEN,
+            (calendarserver_namespace, "notifications-collection-no-client-resources")
+        )
 
-            name = "%s.xml" % (md5.new(str(self) + str(time.time()) + collectionURL).hexdigest(),)
-            path = os.path.join(collection.fp.path, name)
-    
-            # Create new resource in the collection
-            child = NotificationFile(path=path)
-            collection.putChild(name, child)
-            d = waitForDeferred(request.locateChildResource(collection, name))    # This ensures the URI for the resource is mapped
-            yield d
-            child = d.getResult()
-
-            d = waitForDeferred(child.create(request, self))
-            yield d
-            d.getResult()
-        
-    doNotification = deferredGenerator(doNotification)
-
-class NotificationCollectionResource (DAVResource):
-    def resourceType(self):
-        return davxml.ResourceType(
-            davxml.ResourceType.collection,
-            davxml.ResourceType.notifications,
+    def http_MKCALENDAR (self, request):
+        return ErrorResponse(
+            responsecode.FORBIDDEN,
+            (calendarserver_namespace, "notifications-collection-no-client-resources")
         )
 
-    def notify(self):
-        # FIXME: Move doNotification() logic from above class to here
-        pass
-
 class NotificationResource(DAVResource):
     """
     Resource that gets stored in a notification collection and which contains
@@ -163,29 +72,3 @@
         (calendarserver_namespace, "time-stamp"),
         (calendarserver_namespace, "changed"   ),
     )
-
-# FIXME: This needs to be in static.py, but it's referred to in doNotification() above, which is probably incorrect.
-class NotificationFile(NotificationResource, DAVFile):
-    def __init__(self, path):
-        super(NotificationFile, self).__init__(path)
-
-    def create(self, request, notification):
-        """
-        Create the resource, fill out the body, and add properties.
-        """
-        # Create body XML
-        elements = []
-        elements.append(customxml.TimeStamp.fromString(notification.timestamp))
-        elements.append(customxml.Changed(davxml.HRef.fromString(notification.parentURL)))
-                          
-        xml = customxml.Notification(*elements)
-        
-        d = waitForDeferred(put_common.storeResource(request, data=xml.toxml(), destination=self, destination_uri=request.urlForResource(self)))
-        yield d
-        d.getResult()
-
-        # Write properties
-        for element in elements:
-            self.writeDeadProperty(element)
-
-    create = deferredGenerator(create)

Modified: CalendarServer/trunk/twistedcaldav/static.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/static.py	2006-12-19 02:48:43 UTC (rev 852)
+++ CalendarServer/trunk/twistedcaldav/static.py	2006-12-19 02:50:03 UTC (rev 853)
@@ -29,6 +29,8 @@
     "DropBoxHomeFile",
     "DropBoxCollectionFile",
     "DropBoxChildFile",
+    "NotificationsCollectionFile",
+    "NotificationFile",
 ]
 
 import os
@@ -43,6 +45,7 @@
 from twisted.web2.dav.fileop import mkcollection, rmdir
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.idav import IDAVResource
+from twisted.web2.dav.method import put_common as put_common_base
 from twisted.web2.dav.resource import davPrivilegeSet
 from twisted.web2.dav.util import parentForURL, bindMethods
 
@@ -53,6 +56,7 @@
 from twistedcaldav.ical import Component as iComponent
 from twistedcaldav.ical import Property as iProperty
 from twistedcaldav.index import Index, IndexSchedule, db_basename
+from twistedcaldav.notifications import NotificationsCollectionResource, NotificationResource
 from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource, isPseudoCalendarCollectionResource
 from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
 from twistedcaldav.dropbox import DropBoxHomeResource, DropBoxCollectionResource, DropBoxChildResource
@@ -447,16 +451,18 @@
     def provisionChild(self, name):
         if config.DropBoxEnabled:
             DropBoxHomeFileClass = DropBoxHomeFile
-            #NotificationsCollectionFileClass = NotificationsCollectionFile
         else:
             DropBoxHomeFileClass = None
-            #NotificationsCollectionFileClass = None
-
+        if config.NotificationsEnabled:
+            NotificationsCollectionFileClass = NotificationsCollectionFile
+        else:
+            NotificationsCollectionFileClass = None
+            
         cls = {
             "inbox"        : ScheduleInboxFile,
             "outbox"       : ScheduleOutboxFile,
             "dropbox"      : DropBoxHomeFileClass,
-            #"notifications": NotificationsCollectionFileClass,
+            "notifications": NotificationsCollectionFileClass,
         }.get(name, None)
 
         if cls is not None:
@@ -550,6 +556,9 @@
         else:
             return DropBoxCollectionFile(path, self)
 
+    def __repr__(self):
+        return "<%s (dropbox home collection): %s>" % (self.__class__.__name__, self.fp.path)
+
 class DropBoxCollectionFile (DropBoxCollectionResource, CalDAVFile):
     def __init__(self, path, parent):
         DropBoxCollectionResource.__init__(self)
@@ -561,6 +570,9 @@
         else:
             return DropBoxChildFile(path, self)
 
+    def __repr__(self):
+        return "<%s (dropbox collection): %s>" % (self.__class__.__name__, self.fp.path)
+
     http_DELETE =              DropBoxCollectionResource.http_DELETE
     http_PUT =                 DropBoxCollectionResource.http_PUT
     http_MKCALENDAR =          DropBoxCollectionResource.http_MKCALENDAR
@@ -584,6 +596,50 @@
     http_MKCALENDAR = DropBoxChildResource.http_MKCALENDAR
     http_PUT =        DropBoxChildResource.http_PUT
 
+class NotificationsCollectionFile (AutoProvisioningFileMixIn, NotificationsCollectionResource, CalDAVFile):
+    def __init__(self, path, parent):
+        NotificationsCollectionResource.__init__(self)
+        CalDAVFile.__init__(self, path, principalCollections=parent.principalCollections())
+        self._parent = parent
+
+    def createSimilarFile(self, path):
+        if path == self.fp.path:
+            return self
+        else:
+            return NotificationFile(path, self)
+
+    def __repr__(self):
+        return "<%s (notifications collection): %s>" % (self.__class__.__name__, self.fp.path)
+
+    http_PUT =                 NotificationsCollectionResource.http_PUT
+    http_MKCOL =               NotificationsCollectionResource.http_MKCOL
+    http_MKCALENDAR =          NotificationsCollectionResource.http_MKCALENDAR
+
+class NotificationFile(NotificationResource, DAVFile):
+    def __init__(self, path, parent):
+        super(NotificationFile, self).__init__(path, principalCollections=parent.principalCollections())
+
+    def create(self, request, timestamp, parentURL):
+        """
+        Create the resource, fill out the body, and add properties.
+        """
+        # Create body XML
+        elements = []
+        elements.append(customxml.TimeStamp.fromString(timestamp))
+        elements.append(customxml.Changed(davxml.HRef.fromString(parentURL)))
+                          
+        xml = customxml.Notification(*elements)
+        
+        d = waitForDeferred(put_common_base.storeResource(request, data=xml.toxml(), destination=self, destination_uri=request.urlForResource(self)))
+        yield d
+        d.getResult()
+
+        # Write properties
+        for element in elements:
+            self.writeDeadProperty(element)
+
+    create = deferredGenerator(create)
+
 ##
 # Utilities
 ##

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061218/59597c81/attachment.html


More information about the calendarserver-changes mailing list