[CalendarServer-changes] [9232] CalendarServer/trunk/txdav

source_changes at macosforge.org source_changes at macosforge.org
Tue May 8 14:26:12 PDT 2012


Revision: 9232
          http://trac.macosforge.org/projects/calendarserver/changeset/9232
Author:   glyph at apple.com
Date:     2012-05-08 14:26:12 -0700 (Tue, 08 May 2012)
Log Message:
-----------
Add Calendar.asShared(), a nice data-store API for inspecting the sharee's view of a given calendar.

Modified Paths:
--------------
    CalendarServer/trunk/txdav/caldav/datastore/file.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/common.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/caldav/icalendarstore.py
    CalendarServer/trunk/txdav/carddav/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/file.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/icommondatastore.py

Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -265,7 +265,16 @@
         return ResourceType.calendar #@UndefinedVariable
 
 
+    def asShared(self):
+        """
+        Stub for interface-compliance tests.
+        """
+        # TODO: implement me.
+        raise NotImplementedError()
+
+
     ownerCalendarHome = CommonHomeChild.ownerHome
+    viewerCalendarHome = CommonHomeChild.viewerHome
     calendarObjects = CommonHomeChild.objectResources
     listCalendarObjects = CommonHomeChild.listObjectResources
     calendarObjectWithName = CommonHomeChild.objectResourceWithName

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -51,7 +51,7 @@
 from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject,\
     IAttachment
 from txdav.common.datastore.sql import CommonHome, CommonHomeChild,\
-    CommonObjectResource
+    CommonObjectResource, CommonStoreTransaction, ECALENDARTYPE
 from txdav.common.datastore.sql_legacy import \
     PostgresLegacyIndexEmulator, SQLLegacyCalendarInvites,\
     SQLLegacyCalendarShares, PostgresLegacyInboxIndexEmulator
@@ -314,13 +314,19 @@
                         newname = str(uuid.uuid4())
                     newcal = yield self.createCalendarWithName(newname)
                     yield newcal.setSupportedComponents(support_component)
-            
+
             yield _requireCalendarWithType("VEVENT", "calendar")
             yield _requireCalendarWithType("VTODO", "tasks")
-                
+
+
+
+CalendarHome._register(ECALENDARTYPE)
+
+
+
 class Calendar(CommonHomeChild):
     """
-    File-based implementation of L{ICalendar}.
+    SQL-based implementation of L{ICalendar}.
     """
     implements(ICalendar)
 
@@ -340,26 +346,18 @@
     _revisionsBindTable = CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE
     _objectTable = CALENDAR_OBJECT_TABLE
 
-    def __init__(self, home, name, resourceID, owned):
+    _supportedComponents = None
+
+    def __init__(self, *args, **kw):
         """
         Initialize a calendar pointing at a record in a database.
-
-        @param name: the name of the calendar resource.
-        @type name: C{str}
-
-        @param home: the home containing this calendar.
-        @type home: L{CalendarHome}
         """
-        super(Calendar, self).__init__(home, name, resourceID, owned)
-
-        if name == 'inbox':
+        super(Calendar, self).__init__(*args, **kw)
+        if self.name() == 'inbox':
             self._index = PostgresLegacyInboxIndexEmulator(self)
         else:
             self._index = PostgresLegacyIndexEmulator(self)
         self._invites = SQLLegacyCalendarInvites(self)
-        self._objectResourceClass = CalendarObject
-        
-        self._supportedComponents = None
 
 
     @classmethod
@@ -405,6 +403,7 @@
 
 
     ownerCalendarHome = CommonHomeChild.ownerHome
+    viewerCalendarHome = CommonHomeChild.viewerHome
     calendarObjects = CommonHomeChild.objectResources
     listCalendarObjects = CommonHomeChild.listObjectResources
     calendarObjectWithName = CommonHomeChild.objectResourceWithName
@@ -1428,3 +1427,4 @@
         return self._modified
 
 
+Calendar._objectResourceClass = CalendarObject

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -1070,6 +1070,27 @@
 
 
     @inlineCallbacks
+    def test_asShared(self):
+        """
+        L{ICalendar.asShared} returns an iterable of all versions of a shared
+        calendar.
+        """
+        cal = yield self.calendarUnderTest()
+        sharedBefore = yield cal.asShared()
+        # It's not shared yet; make sure asShared doesn't include owner version.
+        self.assertEqual(len(sharedBefore), 0)
+        yield self.test_shareWith()
+        # FIXME: don't know why this separate transaction is needed; remove it.
+        yield self.commit()
+        cal = yield self.calendarUnderTest()
+        sharedAfter = yield cal.asShared()
+        self.assertEqual(len(sharedAfter), 1)
+        self.assertEqual(sharedAfter[0].shareMode(), _BIND_MODE_WRITE)
+        self.assertEqual(sharedAfter[0].viewerCalendarHome().uid(),
+                         OTHER_HOME_UID)
+
+
+    @inlineCallbacks
     def test_hasCalendarResourceUIDSomewhereElse(self):
         """
         L{ICalendarHome.hasCalendarResourceUIDSomewhereElse} will determine if

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -470,10 +470,13 @@
         Overridden to be skipped.
         """
 
+
+    # TODO: ideally the file store would support all of this sharing stuff.
     test_shareWith.skip = "Not implemented for file store yet."
     test_shareAgainChangesMode = test_shareWith
     test_unshareWith = test_shareWith
     test_unshareWithInDifferentTransaction = test_shareWith
+    test_asShared = test_shareWith
 
 
     def test_init(self):

Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -26,7 +26,15 @@
 from twisted.internet.interfaces import ITransport
 from txdav.idav import INotifier
 
+# This is pulling in a bit much for an interfaces module, but currently the bind
+# modes are defined in the schema.
 
+from txdav.common.datastore.sql_tables import _BIND_MODE_OWN as BIND_OWN
+from txdav.common.datastore.sql_tables import _BIND_MODE_READ as BIND_READ
+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE as BIND_WRITE
+from txdav.common.datastore.sql_tables import _BIND_MODE_DIRECT as BIND_DIRECT
+
+
 __all__ = [
     # Interfaces
     "ICalendarTransaction",
@@ -36,6 +44,12 @@
 
     # Exceptions
     "QuotaExceeded",
+
+    # Enumerations
+    "BIND_OWN",
+    "BIND_READ",
+    "BIND_WRITE",
+    "BIND_DIRECT",
 ]
 
 
@@ -226,12 +240,18 @@
         Change the name of this calendar.
         """
 
+
     def ownerCalendarHome():
         """
-        Retrieve the calendar home for the owner of this calendar.
-        Calendars may be shared from one (the owner's) calendar home
-        to other (the sharee's) calendar homes.
+        Retrieve the calendar home for the owner of this calendar.  Calendars
+        may be shared from one (the owner's) calendar home to other (the
+        sharee's) calendar homes.
 
+        FIXME: implementations of this method currently do not behave as
+        documented; a sharee's home, rather than the owner's home, may be
+        returned in some cases.  Current usages should likely be changed to use
+        viewerCalendarHome() instead.
+
         @return: an L{ICalendarHome}.
         """
 
@@ -337,7 +357,52 @@
         """
 
 
+    def asShared():
+        """
+        Get a view of this L{ICalendar} as present in everyone's calendar home
+        except for its owner's.
 
+        @return: a L{Deferred} which fires with a list of L{ICalendar}s, each
+            L{ICalendar} as seen by its respective sharee.  This means that its
+            C{shareMode} will be something other than L{BIND_OWN}, and its
+            L{ICalendar.viewerCalendarHome} will return the home of the sharee.
+        """
+
+
+    def shareMode():
+        """
+        The sharing mode of this calendar; one of the C{BIND_*} constants in
+        this module.
+
+        @see: L{ICalendar.viewerCalendarHome}
+        """
+        # TODO: implement this for the file store.
+
+
+    def viewerCalendarHome():
+        """
+        Retrieve the calendar home for the viewer of this calendar.  In other
+        words, the calendar home that this L{ICalendar} was retrieved through.
+
+        For example: if Alice shares her calendar with Bob,
+        C{txn.calendarHomeWithUID("alice") ...
+        .calendarWithName("calendar").viewerCalendarHome()} will return Alice's
+        home, whereas C{txn.calendarHomeWithUID("bob") ...
+        .sharedChildWithName("alice's calendar").viewerCalendarHome()} will
+        return Bob's calendar home.
+
+        @return: (synchronously) the calendar home of the user into which this
+            L{ICalendar} is bound.
+        @rtype: L{ICalendarHome}
+        """
+        # TODO: implement this for the file store.
+
+        # TODO: implement home-child- retrieval APIs to retrieve shared items
+        # from the store; the example in the docstring ought to be
+        # calendarWithName not sharedChildWithName.
+
+
+
 class ICalendarObject(IDataStoreObject):
     """
     Calendar object

Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -47,9 +47,10 @@
     IAddressBookObject
 
 from txdav.common.datastore.sql import CommonHome, CommonHomeChild,\
-    CommonObjectResource
+    CommonObjectResource, EADDRESSBOOKTYPE
 from twext.enterprise.dal.syntax import Delete
 from twext.enterprise.dal.syntax import Insert
+
 from twext.enterprise.dal.syntax import Update
 from twext.enterprise.dal.syntax import utcNowSQL
 from txdav.common.datastore.sql_tables import ADDRESSBOOK_TABLE,\
@@ -136,9 +137,13 @@
 
 
 
+AddressBookHome._register(EADDRESSBOOKTYPE)
+
+
+
 class AddressBook(CommonHomeChild):
     """
-    File-based implementation of L{IAddressBook}.
+    SQL-based implementation of L{IAddressBook}.
     """
     implements(IAddressBook)
 
@@ -157,27 +162,10 @@
     _revisionsBindTable = ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE
     _objectTable = ADDRESSBOOK_OBJECT_TABLE
 
-    def __init__(self, home, name, resourceID, owned):
-        """
-        Initialize an addressbook pointing at a path on disk.
-
-        @param name: the subdirectory of addressbookHome where this addressbook
-            resides.
-        @type name: C{str}
-
-        @param addressbookHome: the home containing this addressbook.
-        @type addressbookHome: L{AddressBookHome}
-
-        @param realName: If this addressbook was just created, the name which it
-        will eventually have on disk.
-        @type realName: C{str}
-        """
-
-        super(AddressBook, self).__init__(home, name, resourceID, owned)
-
+    def __init__(self, *args, **kw):
+        super(AddressBook, self).__init__(*args, **kw)
         self._index = PostgresLegacyABIndexEmulator(self)
         self._invites = SQLLegacyAddressBookInvites(self)
-        self._objectResourceClass = AddressBookObject
 
 
     @property
@@ -340,3 +328,4 @@
 
 
 
+AddressBook._objectResourceClass = AddressBookObject

Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/common/datastore/file.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -36,7 +36,7 @@
 from twistedcaldav.notifications import NotificationRecord
 from twistedcaldav.notifications import NotificationsDatabase as OldNotificationIndex
 from twistedcaldav.sharing import SharedCollectionsDatabase
-from txdav.caldav.icalendarstore import ICalendarStore
+from txdav.caldav.icalendarstore import ICalendarStore, BIND_OWN
 
 from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
     HomeChildNameAlreadyExistsError, NoSuchHomeChildError, \
@@ -119,7 +119,7 @@
         """
         Create a new transaction.
 
-        @see Transaction
+        @see: L{Transaction}
         """
         return self._transactionClass(
             self,
@@ -693,6 +693,14 @@
         return self._path.basename()
 
 
+    def shareMode(self):
+        """
+        Stub implementation of L{ICalendar.shareMode}; always returns
+        L{BIND_OWN}.
+        """
+        return BIND_OWN
+
+
     _renamedName = None
 
     @writeOperation
@@ -750,10 +758,15 @@
 
         self.notifyChanged()
 
+
     def ownerHome(self):
         return self._home
 
 
+    def viewerHome(self):
+        return self._home
+
+
     def setSharingUID(self, uid):
         self.properties()._setPerUserUID(uid)
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -332,20 +332,31 @@
                 self._primaryHomeType = EADDRESSBOOKTYPE
         directlyProvides(self, *extraInterfaces)
 
-        from txdav.caldav.datastore.sql import CalendarHome
-        from txdav.carddav.datastore.sql import AddressBookHome
-        CommonStoreTransaction._homeClass[ECALENDARTYPE] = CalendarHome
-        CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = AddressBookHome
+        self._circularImportHack()
         self._sqlTxn = sqlTxn
         self.paramstyle = sqlTxn.paramstyle
         self.dialect = sqlTxn.dialect
-        
+
         self._stats = TransactionStatsCollector() if self._store.logStats else None
         self.statementCount = 0
         self.iudCount = 0
         self.currentStatement = None
 
 
+    @classmethod
+    def _circularImportHack(cls):
+        """
+        This method is run when the first L{CommonStoreTransaction} is
+        instantiated, to populate class-scope (in other words, global) state
+        that requires importing modules which depend on this one.
+
+        @see: L{CommonHome._register}
+        """
+        if not cls._homeClass:
+            __import__("txdav.caldav.datastore.sql")
+            __import__("txdav.carddav.datastore.sql")
+
+
     def store(self):
         return self._store
 
@@ -387,24 +398,29 @@
 
         return self._homeClass[storeType].homeWithUID(self, uid, create)
 
+
     @inlineCallbacks
-    def calendarHomeWithResourceID(self, rid):
-        uid = (yield self._homeClass[ECALENDARTYPE].homeUIDWithResourceID(self, rid))
+    def homeWithResourceID(self, storeType, rid, create=False):
+        """
+        Load a calendar or addressbook home by its integer resource ID.
+        """
+        uid = (yield self._homeClass[storeType]
+               .homeUIDWithResourceID(self, rid))
         if uid:
-            result = (yield self.calendarHomeWithUID(uid))
+            result = (yield self.homeWithUID(storeType, uid, create))
         else:
             result = None
         returnValue(result)
 
-    @inlineCallbacks
+
+    def calendarHomeWithResourceID(self, rid):
+        return self.homeWithResourceID(ECALENDARTYPE, rid)
+
+
     def addressbookHomeWithResourceID(self, rid):
-        uid = (yield self._homeClass[EADDRESSBOOKTYPE].homeUIDWithResourceID(self, rid))
-        if uid:
-            result = (yield self.addressbookHomeWithUID(uid))
-        else:
-            result = None
-        returnValue(result)
+        return self.homeWithResourceID(EADDRESSBOOKTYPE, rid)
 
+
     @memoizedKey("uid", "_notificationHomes")
     def notificationsWithUID(self, uid):
         """
@@ -855,6 +871,16 @@
             self._revisionBindJoinTable["BIND:%s" % (key,)] = value
 
 
+    @classmethod
+    def _register(cls, homeType):
+        """
+        Register a L{CommonHome} subclass as its respective home type constant
+        with L{CommonStoreTransaction}.
+        """
+        cls._homeType = homeType
+        CommonStoreTransaction._homeClass[cls._homeType] = cls
+
+
     def quotaAllowedBytes(self):
         return self._txn.store().quota
 
@@ -1827,7 +1853,7 @@
     _objectTable         = None
 
 
-    def __init__(self, home, name, resourceID, owned):
+    def __init__(self, home, name, resourceID, owned, mode):
 
         if home._notifiers:
             childID = "%s/%s" % (home.uid(), name)
@@ -1840,6 +1866,7 @@
         self._name              = name
         self._resourceID        = resourceID
         self._owned             = owned
+        self._bindMode          = mode
         self._created           = None
         self._modified          = None
         self._objects           = {}
@@ -1927,8 +1954,8 @@
         else:
             ownedPiece = (bind.BIND_MODE != _BIND_MODE_OWN).And(
                 bind.BIND_STATUS == _BIND_STATUS_ACCEPTED)
-        
-        columns = [child.RESOURCE_ID, bind.RESOURCE_NAME,]
+
+        columns = [child.RESOURCE_ID, bind.RESOURCE_NAME, bind.BIND_MODE]
         columns.extend(cls.metadataColumns())
         return Select(columns,
                      From=child.join(
@@ -2053,14 +2080,62 @@
         returnValue(resourceName)
 
 
+    def shareMode(self):
+        """
+        @see: L{ICalendar.shareMode}
+        """
+        return self._bindMode
+
+
+    @classproperty
+    def _bindEntriesFor(cls):
+        bind = cls._bindSchema
+        return Select([bind.BIND_MODE, bind.HOME_RESOURCE_ID,
+                       bind.RESOURCE_NAME],
+                      From=bind,
+                      Where=(bind.RESOURCE_ID == Parameter("resourceID")).And
+                            (bind.BIND_STATUS == _BIND_STATUS_ACCEPTED).And
+                            (bind.BIND_MODE != _BIND_MODE_OWN))
+
+
+    @inlineCallbacks
+    def asShared(self):
+        """
+        Retrieve all the versions of this L{CommonHomeChild} as it is shared to
+        everyone.
+
+        @see: L{ICalendarHome.asShared}
+
+        @return: L{CommonHomeChild} objects that represent this
+            L{CommonHomeChild} as a child of different L{CommonHome}s
+        @rtype: a L{Deferred} which fires with a L{list} of L{ICalendar}s.
+        """
+        rows = yield self._bindEntriesFor.on(self._txn,
+                                             resourceID=self._resourceID)
+        cls = self.__class__ # for ease of grepping...
+        result = []
+        for mode, homeResourceID, sharedResourceName in rows:
+            # TODO: this could all be issued in parallel; no need to serialize
+            # the loop.
+            new = cls(
+                (yield self._txn.homeWithResourceID(self._home._homeType,
+                                                    homeResourceID)),
+                sharedResourceName, self._resourceID, False, mode
+            )
+            yield new.initFromStore()
+            result.append(new)
+        returnValue(result)
+
+
     @classmethod
     @inlineCallbacks
     def loadAllObjects(cls, home, owned):
         """
-        Load all child objects and return a list of them. This must create the
-        child classes and initialize them using "batched" SQL operations to keep
-        this constant wrt the number of children. This is an optimization for
-        Depth:1 operations on the home.
+        Load all L{CommonHomeChild} instances which are children of a given
+        L{CommonHome} and return a L{Deferred} firing a list of them.  This must
+        create the child classes and initialize them using "batched" SQL
+        operations to keep this constant wrt the number of children.  This is an
+        optimization for Depth:1 operations on the home.
         """
         results = []
 
@@ -2097,9 +2172,9 @@
 
         # Create the actual objects merging in properties
         for items in dataRows:
-            resourceID, resource_name = items[:2]
-            metadata = items[2:]
-            child = cls(home, resource_name, resourceID, owned)
+            resourceID, resourceName, bindMode = items[:3]
+            metadata = items[3:]
+            child = cls(home, resourceName, resourceID, owned, bindMode)
             for attr, value in zip(cls.metadataAttributes(), metadata):
                 setattr(child, attr, value)
             child._syncTokenRevision = revisions[resourceID]
@@ -2118,7 +2193,7 @@
         """
         bind = cls._bindSchema
         return Select(
-            [bind.RESOURCE_ID],
+            [bind.RESOURCE_ID, bind.BIND_MODE],
             From=bind,
             Where=(bind.RESOURCE_NAME == Parameter('objectName')).And(
                    bind.HOME_RESOURCE_ID == Parameter('homeID')).And(
@@ -2187,8 +2262,8 @@
         if not data:
             returnValue(None)
 
-        resourceID = data[0][0]
-        child = cls(home, name, resourceID, owned)
+        resourceID, mode = data[0]
+        child = cls(home, name, resourceID, owned, mode)
         yield child.initFromStore()
         returnValue(child)
 
@@ -2223,7 +2298,7 @@
         if not data:
             returnValue(None)
         name, mode = data[0]
-        child = cls(home, name, resourceID, mode == _BIND_MODE_OWN)
+        child = cls(home, name, resourceID, mode == _BIND_MODE_OWN, mode)
         yield child.initFromStore()
         returnValue(child)
 
@@ -2293,7 +2368,7 @@
         )
 
         # Initialize other state
-        child = cls(home, name, resourceID, True)
+        child = cls(home, name, resourceID, True, _BIND_MODE_OWN)
         child._created = _created
         child._modified = _modified
         yield child._loadPropertyStore()
@@ -2457,9 +2532,20 @@
 
 
     def ownerHome(self):
+        """
+        (Don't use this method.  See interface documentation as to why.)
+        """
         return self._home
 
 
+    def viewerHome(self):
+        """
+        @see: L{ICalendar.viewerCalendarHome}
+        @see: L{IAddressbook.viewerAddressbookHome}
+        """
+        return self._home
+
+
     @classproperty
     def _ownerHomeFromResourceQuery(cls): #@NoSelf
         """
@@ -2476,6 +2562,12 @@
 
     @inlineCallbacks
     def sharerHomeID(self):
+        """
+        Retrieve the resource ID of the owner of this home.
+
+        @return: a L{Deferred} that fires with the resource ID.
+        @rtype: L{Deferred} firing L{int}
+        """
         if self._owned:
             # If this was loaded by its owner then we can skip the query, since
             # we already know who the owner is.

Modified: CalendarServer/trunk/txdav/common/icommondatastore.py
===================================================================
--- CalendarServer/trunk/txdav/common/icommondatastore.py	2012-05-07 18:12:03 UTC (rev 9231)
+++ CalendarServer/trunk/txdav/common/icommondatastore.py	2012-05-08 21:26:12 UTC (rev 9232)
@@ -236,7 +236,7 @@
         This is a temporary shim method due to the way L{twistedcaldav.sharing}
         works, which is that it expects to look in the 'sharesDB' object to
         find what calendars are shared by whom, separately looks up the owner's
-        calendar home based on that information, then sets the sharee's UID on
+        calendar home based on  that information, then sets the sharee's UID on
         that calendar, the main effect of which is to change the per-user uid
         of the properties for that calendar object.
 
@@ -246,5 +246,5 @@
         end can tell it's shared.
 
         @param shareeUID: the UID of the sharee.
-        @type shareeUID: C{str]
+        @type shareeUID: C{str}
         """
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120508/a226fad6/attachment-0001.html>


More information about the calendarserver-changes mailing list