[CalendarServer-changes] [7413] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Wed May 4 13:16:35 PDT 2011
Revision: 7413
http://trac.macosforge.org/projects/calendarserver/changeset/7413
Author: cdaboo at apple.com
Date: 2011-05-04 13:16:35 -0700 (Wed, 04 May 2011)
Log Message:
-----------
Support default addressbook behavior.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/carddavxml.py
CalendarServer/trunk/twistedcaldav/directory/principal.py
CalendarServer/trunk/twistedcaldav/method/copymove.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/storebridge.py
Modified: CalendarServer/trunk/twistedcaldav/carddavxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/carddavxml.py 2011-05-04 20:10:51 UTC (rev 7412)
+++ CalendarServer/trunk/twistedcaldav/carddavxml.py 2011-05-04 20:16:35 UTC (rev 7413)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -500,6 +500,14 @@
"""
name = "directory"
+class DefaultAddressBookURL (CardDAVElement):
+ """
+ A single href indicating which addressbook is the default.
+ """
+ name = "default-addressbook-URL"
+
+ allowed_children = { (davxml.dav_namespace, "href"): (0, 1) }
+
##
# Extensions to davxml.ResourceType
##
Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py 2011-05-04 20:10:51 UTC (rev 7412)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py 2011-05-04 20:16:35 UTC (rev 7413)
@@ -1001,7 +1001,7 @@
if hasattr(service, "addressBookHomesCollection"):
return service.addressBookHomesCollection.homeForDirectoryRecord(self.record, request)
else:
- return None
+ return succeed(None)
##
# Static
Modified: CalendarServer/trunk/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/copymove.py 2011-05-04 20:10:51 UTC (rev 7412)
+++ CalendarServer/trunk/twistedcaldav/method/copymove.py 2011-05-04 20:16:35 UTC (rev 7413)
@@ -39,7 +39,8 @@
)
from twistedcaldav.resource import isCalendarCollectionResource,\
- isPseudoCalendarCollectionResource, CalDAVResource
+ isPseudoCalendarCollectionResource, CalDAVResource,\
+ isAddressBookCollectionResource
log = Logger()
@@ -131,6 +132,8 @@
if not result:
is_calendar_collection = isPseudoCalendarCollectionResource(self)
defaultCalendar = (yield self.isDefaultCalendar(request)) if is_calendar_collection else False
+ is_addressbook_collection = isAddressBookCollectionResource(self)
+ defaultAddressBook = (yield self.isDefaultAddressBook(request)) if is_addressbook_collection else False
if not is_calendar_collection:
result = yield maybeMOVEContact(self, request)
@@ -140,9 +143,13 @@
# Do default WebDAV action
result = (yield super(CalDAVResource, self).http_MOVE(request))
- if is_calendar_collection:
- # Do some clean up
- yield self.movedCalendar(request, defaultCalendar, destination, destination_uri)
+ if result == responsecode.NO_CONTENT:
+ if is_calendar_collection:
+ # Do some clean up
+ yield self.movedCalendar(request, defaultCalendar, destination, destination_uri)
+ elif is_addressbook_collection:
+ # Do some clean up
+ yield self.movedAddressBook(request, defaultAddressBook, destination, destination_uri)
returnValue(result)
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2011-05-04 20:10:51 UTC (rev 7412)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2011-05-04 20:16:35 UTC (rev 7413)
@@ -1064,14 +1064,6 @@
findSpecialCollections = findSpecialCollectionsFaster
- def createdCalendar(self, request):
- """
- See L{ICalDAVResource.createCalendar}.
- This implementation raises L{NotImplementedError}; a subclass must
- override it.
- """
- unimplemented(self)
-
@inlineCallbacks
def deletedCalendar(self, request):
"""
@@ -1187,6 +1179,35 @@
@inlineCallbacks
+ def movedAddressBook(self, request, defaultAddressBook, destination, destination_uri):
+ """
+ AddressBook has been moved. Need to do some extra clean-up.
+ """
+
+ # Adjust the default addressbook setting if necessary
+ if defaultAddressBook:
+ principal = (yield self.resourceOwnerPrincipal(request))
+ home = (yield principal.addressBookHome(request))
+ (_ignore_scheme, _ignore_host, destination_path, _ignore_query, _ignore_fragment) = urlsplit(normalizeURL(destination_uri))
+ yield home.writeProperty(carddavxml.DefaultAddressBookURL(davxml.HRef(destination_path)), request)
+
+ @inlineCallbacks
+ def isDefaultAddressBook(self, request):
+
+ assert self.isAddressBookCollection()
+
+ # Not allowed to delete the default address book
+ principal = (yield self.resourceOwnerPrincipal(request))
+ home = (yield principal.addressBookHome(request))
+ default = (yield home.readProperty(carddavxml.DefaultAddressBookURL.qname(), request))
+ if default and len(default.children) == 1:
+ defaultURL = normalizeURL(str(default.children[0]))
+ myURL = (yield self.canonicalURL(request))
+ returnValue(defaultURL == myURL)
+
+ returnValue(False)
+
+ @inlineCallbacks
def vCard(self):
"""
See L{ICalDAVResource.vCard}.
@@ -2551,6 +2572,64 @@
returnValue((storeHome, created))
+ def liveProperties(self):
+
+ return super(AddressBookHomeResource, self).liveProperties() + (
+ carddavxml.DefaultAddressBookURL.qname(),
+ )
+
+ @inlineCallbacks
+ def readProperty(self, property, request):
+ if type(property) is tuple:
+ qname = property
+ else:
+ qname = property.qname()
+
+ if qname == carddavxml.DefaultAddressBookURL.qname():
+ # Must have a valid default
+ try:
+ defaultAddressBookProperty = self.readDeadProperty(property)
+ except HTTPError:
+ defaultAddressBookProperty = None
+ if defaultAddressBookProperty and len(defaultAddressBookProperty.children) == 1:
+ defaultAddressBook = str(defaultAddressBookProperty.children[0])
+ adbk = (yield request.locateResource(str(defaultAddressBook)))
+ if adbk is not None and adbk.exists() and isAddressBookCollectionResource(adbk):
+ returnValue(defaultAddressBookProperty)
+
+ # Default is not valid - we have to try to pick one
+ defaultAddressBookProperty = (yield self.pickNewDefaultAddressBook(request))
+ returnValue(defaultAddressBookProperty)
+
+ result = (yield super(AddressBookHomeResource, self).readProperty(property, request))
+ returnValue(result)
+
+ @inlineCallbacks
+ def writeProperty(self, property, request):
+ assert isinstance(property, davxml.WebDAVElement)
+
+ if property.qname() == carddavxml.DefaultAddressBookURL.qname():
+ # Verify that the address book added in the PROPPATCH is valid.
+ property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
+ new_adbk = [str(href) for href in property.children]
+ adbk = None
+ if len(new_adbk) == 1:
+ adbkURI = str(new_adbk[0])
+ adbk = (yield request.locateResource(str(new_adbk[0])))
+ if adbk is None or not adbk.exists() or not isAddressBookCollectionResource(adbk):
+ # Validate that href's point to a valid addressbook.
+ raise HTTPError(ErrorResponse(
+ responsecode.CONFLICT,
+ (carddav_namespace, "valid-default-addressbook-URL"),
+ "Invalid URI",
+ ))
+ else:
+ # Canonicalize the URL to __uids__ form
+ adbkURI = (yield adbk.canonicalURL(request))
+ property = carddavxml.DefaultAddressBookURL(davxml.HRef(adbkURI))
+
+ yield super(AddressBookHomeResource, self).writeProperty(property, request)
+
def _setupProvisions(self):
# Cache children which must be of a specific type
@@ -2590,6 +2669,35 @@
@inlineCallbacks
+ def pickNewDefaultAddressBook(self, request):
+ """
+ First see if "addressbook" exists in the addressbook home and pick that. Otherwise
+ pick the first one we see.
+ """
+ defaultAddressBookURL = joinURL(self.url(), "addressbook")
+ defaultAddressBook = (yield self.makeRegularChild("addressbook"))
+ if defaultAddressBook is None or not defaultAddressBook.exists():
+ getter = iter((yield self._newStoreHome.addressbooks()))
+ # FIXME: the back-end should re-provision a default addressbook here.
+ # Really, the dead property shouldn't be necessary, and this should
+ # be entirely computed by a back-end method like 'defaultAddressBook()'
+ try:
+ anAddressBook = getter.next()
+ except StopIteration:
+ raise RuntimeError("No address books at all.")
+
+ defaultAddressBookURL = joinURL(self.url(), anAddressBook.name())
+
+ self.writeDeadProperty(
+ carddavxml.DefaultAddressBookURL(
+ davxml.HRef(defaultAddressBookURL)
+ )
+ )
+ returnValue(carddavxml.DefaultAddressBookURL(
+ davxml.HRef(defaultAddressBookURL))
+ )
+
+ @inlineCallbacks
def getInternalSyncToken(self):
# The newstore implementation supports this directly
adbktoken = yield self._newStoreHome.syncToken()
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2011-05-04 20:10:51 UTC (rev 7412)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2011-05-04 20:16:35 UTC (rev 7413)
@@ -36,6 +36,7 @@
from twistedcaldav.cache import CacheStoreNotifier, ResponseCacheMixin,\
DisabledCacheNotifier
from twistedcaldav.caldavxml import caldav_namespace
+from twistedcaldav.carddavxml import carddav_namespace
from twistedcaldav.config import config
from twistedcaldav.ical import Component as VCalendar, Property as VProperty,\
InvalidICalendarDataError, iCalendarProductID
@@ -1977,6 +1978,70 @@
returnValue(storer.returndata if hasattr(storer, "returndata") else None)
+ @inlineCallbacks
+ def storeRemove(self, request, viaRequest, where):
+ """
+ Delete this collection resource, first deleting each contained
+ object resource.
+
+ This has to emulate the behavior in fileop.delete in that any errors
+ need to be reported back in a multistatus response.
+
+ @param request: The request used to locate child resources. Note that
+ this is the request which I{triggered} the C{DELETE}, but which may
+ not actually be a C{DELETE} request itself.
+
+ @type request: L{twext.web2.iweb.IRequest}
+
+ @param viaRequest: Indicates if the delete was a direct result of an http_DELETE
+ which for calendars at least will require implicit cancels to be sent.
+
+ @type request: C{bool}
+
+ @param where: the URI at which the resource is being deleted.
+ @type where: C{str}
+
+ @return: an HTTP response suitable for sending to a client (or
+ including in a multi-status).
+
+ @rtype: something adaptable to L{twext.web2.iweb.IResponse}
+ """
+
+ # Not allowed to delete the default address book
+ default = (yield self.isDefaultAddressBook(request))
+ if default:
+ log.err("Cannot DELETE default address book: %s" % (self,))
+ raise HTTPError(ErrorResponse(
+ FORBIDDEN,
+ (carddav_namespace, "default-addressbook-delete-allowed",),
+ "Cannot delete default address book",
+ ))
+
+ response = (
+ yield super(AddressBookCollectionResource, self).storeRemove(
+ request, viaRequest, where
+ )
+ )
+
+ returnValue(response)
+
+ # FIXME: access control
+ @inlineCallbacks
+ def http_MOVE(self, request):
+ """
+ Moving an address book collection is allowed for the purposes of changing
+ that address book's name.
+ """
+ defaultAddressBook = (yield self.isDefaultAddressBook(request))
+
+ result = (yield super(AddressBookCollectionResource, self).http_MOVE(request))
+ if result == NO_CONTENT:
+ destinationURI = urlsplit(request.headers.getHeader("destination"))[2]
+ destination = yield request.locateResource(destinationURI)
+ yield self.movedAddressBook(request, defaultAddressBook,
+ destination, destinationURI)
+ returnValue(result)
+
class GlobalAddressBookCollectionResource(GlobalAddressBookResource, AddressBookCollectionResource):
"""
Wrapper around a L{txdav.carddav.iaddressbook.IAddressBook}.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110504/2ddab964/attachment-0001.html>
More information about the calendarserver-changes
mailing list