[CalendarServer-changes] [5265] CalendarServer/branches/users/cdaboo/shared-calendars-5187/ twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Sat Mar 6 12:03:21 PST 2010
Revision: 5265
http://trac.macosforge.org/projects/calendarserver/changeset/5265
Author: cdaboo at apple.com
Date: 2010-03-06 12:03:21 -0800 (Sat, 06 Mar 2010)
Log Message:
-----------
Proper uninvite and shared collection delete behavior.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -761,7 +761,6 @@
HostURL.qname() : (0, 1),
Organizer.qname() : (0, 1),
InviteSummary.qname() : (0, 1),
- UID.qname() : (0, 1),
}
class ResourceUpdateAdded(davxml.WebDAVEmptyElement):
@@ -809,6 +808,8 @@
allowed_children = {
DTStamp.qname() : (0, None),
+ UID.qname() : (0, None),
+ Sequence.qname() : (0, None),
InviteNotification.qname() : (0, None),
ResourceUpdateNotification.qname() : (0, None),
SharedCalendarUpdateNotification.qname() : (0, None),
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -212,6 +212,12 @@
errors.add(childurl, responsecode.BAD_REQUEST)
# Now do normal delete
+
+ # Handle sharing
+ wasShared = (yield delresource.isShared(self.request))
+ if wasShared:
+ yield delresource.downgradeFromShare(self.request)
+
# Change CTag
yield delresource.bumpSyncToken()
more_responses = (yield self.deleteResource(delresource, deluri, parent))
@@ -346,6 +352,12 @@
errors.add(childurl, responsecode.BAD_REQUEST)
# Now do normal delete
+
+ # Handle sharing
+ wasShared = (yield delresource.isShared(self.request))
+ if wasShared:
+ yield delresource.downgradeFromShare(self.request)
+
yield delresource.updateCTag()
more_responses = (yield self.deleteResource(delresource, deluri, parent))
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/notifications.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -59,7 +59,7 @@
response = (yield super(NotificationResource, self).http_DELETE(request))
if response == responsecode.NO_CONTENT:
- self._parent.deleteNotifictionMessageByName(request, self.resourceName())
+ yield self._parent.removedNotifictionMessage(request, self.resourceName())
returnValue(response)
class NotificationCollectionResource(DAVResource):
@@ -76,17 +76,16 @@
def resourceType(self):
return davxml.ResourceType.notification
+ @inlineCallbacks
def addNotification(self, request, uid, xmltype, xmldata):
# Write data to file
rname = uid + ".xml"
- self._writeNotification(request, uid, rname, xmltype, xmldata)
+ yield self._writeNotification(request, uid, rname, xmltype, xmldata)
# Update database
self.notificationsDB().addOrUpdateRecord(NotificationRecord(uid, rname, xmltype))
- return succeed(None)
-
def _writeNotification(self, request, uid, rname, xmltype, xmldata):
raise NotImplementedError
@@ -96,12 +95,29 @@
def getNotifictionMessageByUID(self, request, uid):
return succeed(self.notificationsDB().recordForUID(uid))
+ @inlineCallbacks
def deleteNotifictionMessageByUID(self, request, uid):
- return succeed(self.notificationsDB().removeRecordForUID(uid))
+
+ # See if it exists and delete the resource
+ record = self.notificationsDB().recordForUID(uid)
+ if record:
+ yield self._deleteNotification(request, record.name)
+ self.notificationsDB().removeRecordForUID(record.uid)
def deleteNotifictionMessageByName(self, request, rname):
- return succeed(self.notificationsDB().removeRecordForName(rname))
+ # See if it exists and delete the resource
+ record = self.notificationsDB().recordForName(rname)
+ if record:
+ self._deleteNotification(request, record.name)
+ self.notificationsDB().removeRecordForUID(record.uid)
+
+ return succeed(None)
+
+ def removedNotifictionMessage(self, request, rname):
+ self.notificationsDB().removeRecordForName(rname)
+ return succeed(None)
+
class NotificationRecord(object):
def __init__(self, uid, name, xmltype):
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -69,7 +69,7 @@
from twistedcaldav.ical import Component as iComponent
from twistedcaldav.ical import allowedComponents
from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
-from twistedcaldav.sharing import SharingMixin
+from twistedcaldav.sharing import SharedCollectionMixin
from twistedcaldav.vcard import Component as vComponent
@@ -135,7 +135,7 @@
return fun
-class CalDAVResource (CalDAVComplianceMixIn, SharingMixin, DAVResource, LoggingMixIn):
+class CalDAVResource (CalDAVComplianceMixIn, SharedCollectionMixin, DAVResource, LoggingMixIn):
"""
CalDAV resource.
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -31,7 +31,7 @@
import types
__all__ = [
- "SharingMixin",
+ "SharedCollectionMixin",
]
from twistedcaldav import customxml
@@ -41,7 +41,7 @@
Sharing behavior
"""
-class SharingMixin(object):
+class SharedCollectionMixin(object):
def invitesDB(self):
@@ -79,20 +79,24 @@
return succeed(True)
+ @inlineCallbacks
def downgradeFromShare(self, request):
- # Change resource type
+ # Change resource type (note this might be called after deleting a resource
+ # so we have to cope with that)
rtype = self.resourceType()
rtype = davxml.ResourceType(*([child for child in rtype.children if child != customxml.SharedOwner()]))
self.writeDeadProperty(rtype)
# Remove all invitees
+ records = self.invitesDB().allRecords()
+ yield self.uninviteUserToShare([record.userid for record in records], None, request)
# Remove invites database
self.invitesDB().remove()
delattr(self, "_invitesDB")
- return succeed(True)
+ returnValue(True)
def removeUserFromInvite(self, userid, request):
""" Remove a user from this shared calendar """
@@ -236,14 +240,28 @@
returnValue(True)
+ @inlineCallbacks
def uninviteSingleUserFromShare(self, userid, aces, request):
+ newuserid = self.validUserIDForShare(userid)
+ if newuserid:
+ userid = newuserid
+
# Cancel invites
+ record = self.invitesDB().recordForUserID(userid)
+
+ # If current user state is accepted then we send an invite with the new state, otherwise
+ # we cancel any existing invites for the user
+ if record and record.state != "ACCEPTED":
+ yield self.removeInvite(record, request)
+ elif record:
+ record.state = "DELETED"
+ yield self.sendInvite(record, request)
# Remove from database
self.invitesDB().removeRecordForUserID(userid)
- return succeed(True)
+ returnValue(True)
def inviteSingleUserUpdateToShare(self, userid, acesOLD, aceNEW, summary, request, commonName="", shareName=""):
@@ -273,6 +291,8 @@
xmltype = customxml.InviteNotification.name
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](),
@@ -285,14 +305,24 @@
davxml.HRef.fromString(owner),
),
customxml.InviteSummary.fromString(record.summary),
- customxml.UID.fromString(record.inviteuid),
- customxml.Sequence.fromString(str(record.sequence)),
),
).toxml()
# Add to collections
yield notifications.addNotification(request, record.inviteuid, xmltype, xmldata)
+ @inlineCallbacks
+ def removeInvite(self, record, request):
+
+ # Locate notifications collection for user
+ sharee = self.principalForCalendarUserAddress(record.userid)
+ if sharee is None:
+ raise ValueError("sharee is None but userid was valid before")
+ notifications = (yield request.locateResource(sharee.notificationURL()))
+
+ # Add to collections
+ yield notifications.deleteNotifictionMessageByUID(request, record.inviteuid)
+
def xmlPOSTNoAuth(self, encoding, request):
def _handleErrorResponse(error):
if isinstance(error.value, HTTPError) and hasattr(error.value, "response"):
@@ -479,6 +509,94 @@
("text", "xml") : xmlPOSTAuth,
}
+class SharedHomeMixin(object):
+ """
+ A mix-in for calendar/addressbook homes that defines the operations for manipulating a sharee's
+ set of shared calendfars.
+ """
+
+ def acceptShare(self, request, hostUrl, displayname=None):
+ """ Accept an invite to a shared calendar """
+ return succeed(True)
+
+ def wouldAcceptShare(self, hostUrl, request):
+ return succeed(True)
+
+ def removeShare(self, hostUrl, resourceName, request):
+ """ Remove a shared calendar named in resourceName """
+ return succeed(True)
+
+ def declineShare(self, hostUrl, request):
+ return succeed(True)
+
+ def xmlPOSTNoAuth(self, encoding, request):
+ def _handleResponse(result):
+ response = Response(code=responsecode.OK)
+ return response
+
+ def _handleErrorResponse(error):
+ if isinstance(error.value, HTTPError) and hasattr(error.value, "response"):
+ return error.value.response
+ return Response(code=responsecode.BAD_REQUEST)
+
+ def _handleInviteUpdate(inviteupdatedoc):
+ """ Handle a user accepting or declining a sharing invite """
+ hostUrl = None
+ accepted = None
+ summary = None
+ for item in inviteupdatedoc.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
+ 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
+
+ if accepted is not None:
+ if hostUrl:
+ if accepted:
+ return self.acceptShare(request, hostUrl, displayname=summary)
+ else:
+ return self.declineShare(hostUrl, request)
+ else:
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (customxml.calendarserver_namespace, "valid-request-content"),
+ "missing hosturl",
+ ))
+ else:
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (customxml.calendarserver_namespace, "valid-request-content"),
+ "missing invite status",
+ ))
+
+ def _getData(data):
+ try:
+ doc = davxml.WebDAVDocument.fromString(data)
+ except ValueError, e:
+ print "Error parsing doc (%s) Doc:\n %s" % (str(e), data,)
+ raise
+
+ root = doc.root_element
+ xmlDocHanders = {
+ customxml.InviteNotification: _handleInviteUpdate,
+ }
+ if type(root) in xmlDocHanders:
+ return xmlDocHanders[type(root)](root).addCallbacks(_handleResponse, errback=_handleErrorResponse)
+ else:
+ return fail(True)
+
+ return allDataFromStream(request.stream).addCallback(_getData)
+
inviteAccessMapToXML = {
"read-only" : customxml.ReadAccess,
"read-write" : customxml.ReadWriteAccess,
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py 2010-03-06 20:01:57 UTC (rev 5264)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py 2010-03-06 20:03:21 UTC (rev 5265)
@@ -55,7 +55,7 @@
from twext.web2.http import HTTPError, StatusResponse
from twext.web2.dav import davxml
from twext.web2.dav.element.base import dav_namespace
-from twext.web2.dav.fileop import mkcollection, rmdir
+from twext.web2.dav.fileop import mkcollection, rmdir, delete
from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.idav import IDAVResource
from twext.web2.dav.noneprops import NonePropertyStore
@@ -1312,11 +1312,20 @@
def _writeNotification(self, request, uid, rname, xmltype, xmldata):
+ # TODO: use the generic StoreObject api so that quota, sync-token etc all get changed properly
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))
+
+ return succeed(True)
+ def _deleteNotification(self, request, rname):
+
+ # TODO: use the generic DeleteResource api so that quota, sync-token etc all get changed properly
+ childfp = self.fp.child(rname)
+ return delete("", childfp)
+
class NotificationFile(NotificationResource, CalDAVFile):
def __init__(self, path, parent):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100306/206d562d/attachment-0001.html>
More information about the calendarserver-changes
mailing list