[CalendarServer-changes] [8214] CalendarServer/branches/users/cdaboo/component-set-fixes
source_changes at macosforge.org
source_changes at macosforge.org
Thu Oct 20 10:45:24 PDT 2011
Revision: 8214
http://trac.macosforge.org/projects/calendarserver/changeset/8214
Author: cdaboo at apple.com
Date: 2011-10-20 10:45:22 -0700 (Thu, 20 Oct 2011)
Log Message:
-----------
Handle default calendar behavior for tasks-only calendar.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/customxml.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/copymove.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcalendar.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcol.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/resource.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/schedule.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/stdconfig.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_props.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_schedule.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_sharing.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_validation.py
CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_wrapping.py
CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/file.py
CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/common.py
CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/util.py
CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/test/util.py
CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/upgrade/test/test_migrate.py
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/customxml.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/customxml.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -518,6 +518,15 @@
(calendarserver_namespace, "action" ) : (0, 1), # Have to allow 0 as element is empty in PROPFIND requests
}
+class ScheduleDefaultTasksURL (davxml.WebDAVElement):
+ """
+ A single href indicating which calendar is the default for VTODO scheduling.
+ """
+ namespace = calendarserver_namespace
+ name = "schedule-default-tasks-URL"
+
+ allowed_children = { (davxml.dav_namespace, "href"): (0, 1) }
+
class DTStamp (davxml.WebDAVTextElement):
"""
A UTC timestamp in iCal format.
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/copymove.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/copymove.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -131,7 +131,7 @@
result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
if not result:
is_calendar_collection = isPseudoCalendarCollectionResource(self)
- defaultCalendar = (yield self.isDefaultCalendar(request)) if is_calendar_collection else False
+ defaultCalendarType = (yield self.isDefaultCalendar(request)) if is_calendar_collection else None
is_addressbook_collection = isAddressBookCollectionResource(self)
defaultAddressBook = (yield self.isDefaultAddressBook(request)) if is_addressbook_collection else False
@@ -146,7 +146,7 @@
if result == responsecode.NO_CONTENT:
if is_calendar_collection:
# Do some clean up
- yield self.movedCalendar(request, defaultCalendar, destination, destination_uri)
+ yield self.movedCalendar(request, defaultCalendarType, destination, destination_uri)
elif is_addressbook_collection:
# Do some clean up
yield self.movedAddressBook(request, defaultAddressBook, destination, destination_uri)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcalendar.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcalendar.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.config import config
"""
CalDAV MKCALENDAR method.
@@ -76,6 +77,7 @@
log.err("Error while handling MKCALENDAR: %s" % (e,))
raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
+ set_supported_component_set = False
if doc is not None:
makecalendar = doc.root_element
if not isinstance(makecalendar, caldavxml.MakeCalendar):
@@ -92,7 +94,8 @@
for property in makecalendar.children[0].children[0].children:
try:
if property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
- self.setSupportedComponentSet(property)
+ yield self.setSupportedComponentSet(property)
+ set_supported_component_set = True
else:
yield self.writeProperty(property, request)
except HTTPError:
@@ -106,5 +109,10 @@
self.transactionError()
errors.error()
raise HTTPError(MultiStatusResponse([errors.response()]))
+
+ # When calendar collections are single component only, default MKCALENDAR is VEVENT only
+ if not set_supported_component_set and config.CalDAV.AccountProvisioning.KeepComponentTypesSeparate:
+ yield self.setSupportedComponents(("VEVENT",))
+
returnValue(responsecode.CREATED)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcol.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcol.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/method/mkcol.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -122,6 +122,7 @@
errors = PropertyStatusResponseQueue("PROPPATCH", request.uri, responsecode.NO_CONTENT)
got_an_error = False
+ set_supported_component_set = False
if mkcol.children:
# mkcol -> set -> prop -> property*
properties = mkcol.children[0].children[0].children
@@ -166,7 +167,8 @@
for property in mkcol.children[0].children[0].children:
try:
if rtype == "calendar" and property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
- self.setSupportedComponentSet(property)
+ yield self.setSupportedComponentSet(property)
+ set_supported_component_set = True
else:
yield self.writeProperty(property, request)
except HTTPError:
@@ -183,6 +185,10 @@
code=responsecode.BAD_REQUEST,
stream=mkcolxml.MakeCollectionResponse(errors.response()).toxml()
))
+
+ # When calendar collections are single component only, default MKCALENDAR is VEVENT only
+ if rtype == "calendar" and not set_supported_component_set and config.CalDAV.AccountProvisioning.KeepComponentTypesSeparate:
+ yield self.setSupportedComponents(("VEVENT",))
yield returnValue(responsecode.CREATED)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/resource.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/resource.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -590,10 +590,10 @@
url = (yield self.canonicalURL(request))
returnValue(davxml.AddMember(davxml.HRef.fromString(url + "/;add-member")))
- elif qname == caldavxml.SupportedCalendarComponentSet.qname():
+ elif qname == caldavxml.SupportedCalendarComponentSet.qname() and self.isPseudoCalendarCollection():
returnValue(self.getSupportedComponentSet())
- elif qname == caldavxml.SupportedCalendarData.qname():
+ elif qname == caldavxml.SupportedCalendarData.qname() and self.isPseudoCalendarCollection():
returnValue(caldavxml.SupportedCalendarData(
caldavxml.CalendarData(**{
"content-type": "text/calendar",
@@ -601,19 +601,19 @@
}),
))
- elif qname == caldavxml.MaxResourceSize.qname():
+ elif qname == caldavxml.MaxResourceSize.qname() and self.isPseudoCalendarCollection():
if config.MaxResourceSize:
returnValue(caldavxml.MaxResourceSize.fromString(
str(config.MaxResourceSize)
))
- elif qname == caldavxml.MaxInstances.qname():
+ elif qname == caldavxml.MaxInstances.qname() and self.isPseudoCalendarCollection():
if config.MaxAllowedInstances:
returnValue(caldavxml.MaxInstances.fromString(
str(config.MaxAllowedInstances)
))
- elif qname == caldavxml.MaxAttendeesPerInstance.qname():
+ elif qname == caldavxml.MaxAttendeesPerInstance.qname() and self.isPseudoCalendarCollection():
if config.MaxAttendeesPerInstance:
returnValue(caldavxml.MaxAttendeesPerInstance.fromString(
str(config.MaxAttendeesPerInstance)
@@ -626,10 +626,10 @@
self.scheduleTag
))
- elif qname == caldavxml.ScheduleCalendarTransp.qname():
+ elif qname == caldavxml.ScheduleCalendarTransp.qname() and self.isCalendarCollection():
# For backwards compatibility, if the property does not exist we need to create
# it and default to the old free-busy-set value.
- if self.isCalendarCollection() and not self.hasDeadProperty(property):
+ if not self.hasDeadProperty(property):
# For backwards compatibility we need to sync this up with the calendar-free-busy-set on the inbox
principal = (yield self.resourceOwnerPrincipal(request))
fbset = (yield principal.calendarFreeBusyURIs(request))
@@ -639,7 +639,7 @@
opaque = url in fbset
self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque() if opaque else caldavxml.Transparent()))
- elif qname == carddavxml.SupportedAddressData.qname():
+ elif qname == carddavxml.SupportedAddressData.qname() and self.isAddressBookCollection():
# CardDAV, section 6.2.2
returnValue(carddavxml.SupportedAddressData(
carddavxml.AddressDataType(**{
@@ -648,7 +648,7 @@
}),
))
- elif qname == carddavxml.MaxResourceSize.qname():
+ elif qname == carddavxml.MaxResourceSize.qname() and self.isAddressBookCollection():
# CardDAV, section 6.2.3
if config.MaxResourceSize:
returnValue(carddavxml.MaxResourceSize.fromString(
@@ -1069,7 +1069,7 @@
inbox.processFreeBusyCalendar(request.path, False)
@inlineCallbacks
- def movedCalendar(self, request, defaultCalendar, destination, destination_uri):
+ def movedCalendar(self, request, defaultCalendarType, destination, destination_uri):
"""
Calendar has been moved. Need to do some extra clean-up.
"""
@@ -1085,8 +1085,8 @@
inbox.processFreeBusyCalendar(destination_uri, destination.isCalendarOpaque())
# Adjust the default calendar setting if necessary
- if defaultCalendar:
- yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(destination_path)), request)
+ if defaultCalendarType is not None:
+ yield inbox.writeProperty(defaultCalendarType(davxml.HRef(destination_path)), request)
def isCalendarOpaque(self):
@@ -1108,13 +1108,10 @@
inboxURL = principal.scheduleInboxURL()
if inboxURL:
inbox = (yield request.locateResource(inboxURL))
- default = (yield inbox.readProperty((caldav_namespace, "schedule-default-calendar-URL"), request))
- if default and len(default.children) == 1:
- defaultURL = normalizeURL(str(default.children[0]))
- myURL = (yield self.canonicalURL(request))
- returnValue(defaultURL == myURL)
+ result = (yield inbox.isDefaultCalendar(request, self))
+ returnValue(result)
- returnValue(False)
+ returnValue(None)
@inlineCallbacks
def iCalendarForUser(self, request):
@@ -2415,7 +2412,7 @@
# this URL live from a back-end method that tells us what the
# default calendar is.
inbox = yield self.getChild("inbox")
- childURL = joinURL(self.url(), "calendar")
+ childURL = joinURL(self.url(), config.CalDAV.AccountProvisioning.CalendarName)
inbox.processFreeBusyCalendar(childURL, True)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/schedule.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/schedule.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -40,11 +40,12 @@
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
-from twistedcaldav import caldavxml
+from twistedcaldav import caldavxml, customxml
from twistedcaldav.caldavxml import caldav_namespace, Opaque,\
CalendarFreeBusySet, ScheduleCalendarTransp
from twistedcaldav.config import config
from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.ical import allowedComponents
from twistedcaldav.extensions import DAVResource
from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn
from twistedcaldav.resource import isCalendarCollectionResource
@@ -135,8 +136,9 @@
def liveProperties(self):
return super(ScheduleInboxResource, self).liveProperties() + (
- (caldav_namespace, "calendar-free-busy-set"),
- (caldav_namespace, "schedule-default-calendar-URL"),
+ caldavxml.CalendarFreeBusySet.qname(),
+ caldavxml.ScheduleDefaultCalendarURL.qname(),
+ customxml.ScheduleDefaultTasksURL.qname(),
)
def resourceType(self):
@@ -149,7 +151,7 @@
else:
qname = property.qname()
- if qname == (caldav_namespace, "calendar-free-busy-set"):
+ if qname == caldavxml.CalendarFreeBusySet.qname():
# Always return at least an empty list
if not self.hasDeadProperty(property):
top = self.parent.url()
@@ -159,22 +161,10 @@
if prop == ScheduleCalendarTransp(Opaque()):
values.append(HRef(joinURL(top, cal.name())))
returnValue(CalendarFreeBusySet(*values))
- elif qname == (caldav_namespace, "schedule-default-calendar-URL"):
- # Must have a valid default
- try:
- defaultCalendarProperty = self.readDeadProperty(property)
- except HTTPError:
- defaultCalendarProperty = None
- if defaultCalendarProperty and len(defaultCalendarProperty.children) == 1:
- defaultCalendar = str(defaultCalendarProperty.children[0])
- cal = (yield request.locateResource(str(defaultCalendar)))
- if cal is not None and isCalendarCollectionResource(cal) and cal.exists() and not cal.isVirtualShare():
- returnValue(defaultCalendarProperty)
+ elif qname in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
+ result = (yield self.readDefaultCalendarProperty(request, qname))
+ returnValue(result)
- # Default is not valid - we have to try to pick one
- defaultCalendarProperty = (yield self.pickNewDefaultCalendar(request))
- returnValue(defaultCalendarProperty)
-
result = (yield super(ScheduleInboxResource, self).readProperty(property, request))
returnValue(result)
@@ -186,7 +176,7 @@
# server enforces what can be stored, however it need not actually
# exist so we cannot list it in liveProperties on this resource, since its
# its presence there means that hasProperty will always return True for it.
- if property.qname() == (calendarserver_namespace, "calendar-availability"):
+ if property.qname() == customxml.CalendarAvailability.qname():
if not property.valid():
raise HTTPError(ErrorResponse(
responsecode.CONFLICT,
@@ -194,7 +184,7 @@
description="Invalid property"
))
- elif property.qname() == (caldav_namespace, "calendar-free-busy-set"):
+ elif property.qname() == caldavxml.CalendarFreeBusySet.qname():
# Verify that the calendars added in the PROPPATCH are valid. We do not check
# whether existing items in the property are still valid - only new ones.
property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
@@ -214,36 +204,18 @@
"Invalid URI",
))
- elif property.qname() == (caldav_namespace, "schedule-default-calendar-URL"):
- # Verify that the calendar added in the PROPPATCH is valid.
- property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
- new_calendar = [str(href) for href in property.children]
- cal = None
- if len(new_calendar) == 1:
- calURI = str(new_calendar[0])
- cal = (yield request.locateResource(str(new_calendar[0])))
- # TODO: check that owner of the new calendar is the same as owner of this inbox
- if cal is None or not cal.exists() or not isCalendarCollectionResource(cal) or cal.isVirtualShare():
- # Validate that href's point to a valid calendar.
- raise HTTPError(ErrorResponse(
- responsecode.CONFLICT,
- (caldav_namespace, "valid-schedule-default-calendar-URL"),
- "Invalid URI",
- ))
- else:
- # Canonicalize the URL to __uids__ form
- calURI = (yield cal.canonicalURL(request))
- property = caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(calURI))
+ elif property.qname() in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
+ property = (yield self.writeDefaultCalendarProperty(request, property))
yield super(ScheduleInboxResource, self).writeProperty(property, request)
def processFreeBusyCalendar(self, uri, addit):
uri = normalizeURL(uri)
- if not self.hasDeadProperty((caldav_namespace, "calendar-free-busy-set")):
+ if not self.hasDeadProperty(caldavxml.CalendarFreeBusySet.qname()):
fbset = set()
else:
- fbset = set([normalizeURL(str(href)) for href in self.readDeadProperty((caldav_namespace, "calendar-free-busy-set")).children])
+ fbset = set([normalizeURL(str(href)) for href in self.readDeadProperty(caldavxml.CalendarFreeBusySet.qname()).children])
if addit:
if uri not in fbset:
fbset.add(uri)
@@ -254,35 +226,94 @@
self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
@inlineCallbacks
- def pickNewDefaultCalendar(self, request):
+ def readDefaultCalendarProperty(self, request, qname):
"""
- First see if "calendar" exists in the calendar home and pick that. Otherwise
- create "calendar" in the calendar home.
+ Read either the default VEVENT or VTODO calendar property. Try to pick one if not present.
"""
+
+ tasks = qname == customxml.ScheduleDefaultTasksURL.qname()
+
+ # Must have a valid default
+ try:
+ defaultCalendarProperty = self.readDeadProperty(qname)
+ except HTTPError:
+ defaultCalendarProperty = None
+ if defaultCalendarProperty and len(defaultCalendarProperty.children) == 1:
+ defaultCalendar = str(defaultCalendarProperty.children[0])
+ cal = (yield request.locateResource(str(defaultCalendar)))
+ if cal is not None and isCalendarCollectionResource(cal) and cal.exists() and not cal.isVirtualShare():
+ returnValue(defaultCalendarProperty)
+
+ # Default is not valid - we have to try to pick one
+ defaultCalendarProperty = (yield self.pickNewDefaultCalendar(request, tasks=tasks))
+ returnValue(defaultCalendarProperty)
+
+ @inlineCallbacks
+ def writeDefaultCalendarProperty(self, request, property):
+ """
+ Write either the default VEVENT or VTODO calendar property, validating and canonicalizing the value
+ """
+ tasks = property.qname() == customxml.ScheduleDefaultTasksURL
+ componentType = "VTODO" if tasks else "VEVENT"
+ prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL
+ error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL") if tasks else (caldav_namespace, "valid-schedule-default-calendar-URL")
+
+ # Verify that the calendar added in the PROPPATCH is valid.
+ property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
+ new_calendar = [str(href) for href in property.children]
+ cal = None
+ if len(new_calendar) == 1:
+ calURI = str(new_calendar[0])
+ cal = (yield request.locateResource(str(new_calendar[0])))
+
+ # TODO: check that owner of the new calendar is the same as owner of this inbox
+ if cal is None or not cal.exists() or not isCalendarCollectionResource(cal) or \
+ cal.isVirtualShare() or not cal.isSupportedComponent(componentType):
+ # Validate that href's point to a valid calendar.
+ raise HTTPError(ErrorResponse(
+ responsecode.CONFLICT,
+ error_element,
+ "Invalid URI",
+ ))
+ else:
+ # Canonicalize the URL to __uids__ form
+ calURI = (yield cal.canonicalURL(request))
+ property = prop_to_set(davxml.HRef(calURI))
+ returnValue(property)
+
+ @inlineCallbacks
+ def pickNewDefaultCalendar(self, request, tasks=False):
+ """
+ First see if default provisioned calendar exists in the calendar home and pick that. Otherwise
+ pick another from the calendar home.
+ """
+
+ componentType = "VTODO" if tasks else "VEVENT"
+ test_name = config.CalDAV.AccountProvisioning.TasksName if tasks else config.CalDAV.AccountProvisioning.CalendarName
+ prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL
+
calendarHomeURL = self.parent.url()
- defaultCalendarURL = joinURL(calendarHomeURL, "calendar")
+ defaultCalendarURL = joinURL(calendarHomeURL, test_name)
defaultCalendar = (yield request.locateResource(defaultCalendarURL))
if defaultCalendar is None or not defaultCalendar.exists():
# FIXME: the back-end should re-provision a default calendar here.
# Really, the dead property shouldn't be necessary, and this should
# be entirely computed by a back-end method like 'defaultCalendar()'
for calendarName in (yield self.parent._newStoreHome.listCalendars()): # These are only unshared children
- if calendarName != "inbox":
- aCalendar = calendarName
- break
+ if calendarName == "inbox":
+ continue
+ calendar = (yield self.parent._newStoreHome.calendarWithName(calendarName))
+ if not calendar.isSupportedComponent(componentType):
+ continue
+ break
else:
- raise RuntimeError("No valid calendars to use as a default calendar.")
+ raise RuntimeError("No valid calendars to use as a default %s calendar." % (componentType,))
- defaultCalendarURL = joinURL(calendarHomeURL, aCalendar)
+ defaultCalendarURL = joinURL(calendarHomeURL, calendarName)
- self.writeDeadProperty(
- caldavxml.ScheduleDefaultCalendarURL(
- davxml.HRef(defaultCalendarURL)
- )
- )
- returnValue(caldavxml.ScheduleDefaultCalendarURL(
- davxml.HRef(defaultCalendarURL))
- )
+ prop = prop_to_set(davxml.HRef(defaultCalendarURL))
+ self.writeDeadProperty(prop)
+ returnValue(prop)
@inlineCallbacks
def defaultCalendar(self, request, componentType):
@@ -292,7 +323,7 @@
"""
# Check any default calendar property first
- default = (yield self.readProperty((caldav_namespace, "schedule-default-calendar-URL"), request))
+ default = (yield self.readProperty(caldavxml.ScheduleDefaultCalendarURL.qname(), request))
if len(default.children) == 1:
defaultURL = str(default.children[0])
default = (yield request.locateResource(defaultURL))
@@ -329,6 +360,24 @@
returnValue(default)
+ @inlineCallbacks
+ def isDefaultCalendar(self, request, calendar):
+ """
+ Is the supplied calendar one of the possible default calendars.
+ """
+ assert calendar.isCalendarCollection()
+
+ # Not allowed to delete the default calendar
+ for default_prop in (caldavxml.ScheduleDefaultCalendarURL, customxml.ScheduleDefaultTasksURL,):
+ default = (yield self.readProperty(default_prop.qname(), request))
+ if default and len(default.children) == 1:
+ defaultURL = normalizeURL(str(default.children[0]))
+ myURL = (yield calendar.canonicalURL(request))
+ if defaultURL == myURL:
+ returnValue(default_prop)
+
+ returnValue(None)
+
##
# ACL
##
@@ -362,6 +411,11 @@
def resourceType(self):
return davxml.ResourceType.scheduleOutbox
+ def getSupportedComponentSet(self):
+ return caldavxml.SupportedCalendarComponentSet(
+ *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
+ )
+
@inlineCallbacks
def http_POST(self, request):
"""
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/stdconfig.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/stdconfig.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -527,8 +527,7 @@
# VEVENTs and VTODOs if KeepComponentTypesSeparate is False
"TasksName": "tasks", # Name to use when provisioning calendar for VTODOs only, not
# provisioned if KeepComponentTypesSeparate is False
- "KeepComponentTypesSeparate" : False, # Provision collections with separate
- # for separate component types
+ "KeepComponentTypesSeparate" : True, # Provision collections with separate component types
},
},
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/storebridge.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/storebridge.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -890,8 +890,53 @@
def notifyChanged(self):
self._newStoreObject.notifyChanged()
-class CalendarCollectionResource(_CommonHomeChildCollectionMixin, CalDAVResource):
+class _CalendarCollectionBehaviorMixin():
"""
+ Functions common to calendar and inbox collections
+ """
+
+ # Support component set behaviors
+ def setSupportedComponentSet(self, support_components_property):
+ """
+ Parse out XML property into list of components and give to store.
+ """
+ support_components = ",".join(sorted([comp.attributes["name"].upper() for comp in support_components_property.children]))
+ return maybeDeferred(self._newStoreObject.setSupportedComponents, support_components)
+
+ def getSupportedComponentSet(self):
+ comps = self._newStoreObject.getSupportedComponents()
+ if comps:
+ comps = comps.split(",")
+ else:
+ comps = allowedComponents
+ return caldavxml.SupportedCalendarComponentSet(
+ *[caldavxml.CalendarComponent(name=item) for item in comps]
+ )
+
+ def setSupportedComponents(self, components):
+ """
+ Set the allowed component set for this calendar.
+
+ @param components: list of names of components to support
+ @type components: C{list}
+ """
+ support_components = ",".join(sorted([comp.upper() for comp in components]))
+ return maybeDeferred(self._newStoreObject.setSupportedComponents, support_components)
+
+ def getSupportedComponents(self):
+ comps = self._newStoreObject.getSupportedComponents()
+ if comps:
+ comps = comps.split(",")
+ else:
+ comps = allowedComponents
+ return comps
+
+ def isSupportedComponent(self, componentType):
+ return self._newStoreObject.isSupportedComponent(componentType)
+
+
+class CalendarCollectionResource(_CalendarCollectionBehaviorMixin, _CommonHomeChildCollectionMixin, CalDAVResource):
+ """
Wrapper around a L{txdav.caldav.icalendar.ICalendar}.
"""
@@ -929,34 +974,6 @@
"""
return True
- def setSupportedComponentSet(self, support_components_property):
- """
- Parse out XML property into list of components and give to store.
- """
- support_components = ",".join(sorted([comp.attributes["name"].upper() for comp in support_components_property.children]))
- return maybeDeferred(self._newStoreObject.setSupportedComponents, support_components)
-
- def getSupportedComponentSet(self):
- comps = self._newStoreObject.getSupportedComponents()
- if comps:
- comps = comps.split(",")
- else:
- comps = allowedComponents
- return caldavxml.SupportedCalendarComponentSet(
- *[caldavxml.CalendarComponent(name=item) for item in comps]
- )
-
- def getSupportedComponents(self):
- comps = self._newStoreObject.getSupportedComponents()
- if comps:
- comps = comps.split(",")
- else:
- comps = allowedComponents
- return comps
-
- def isSupportedComponent(self, componentType):
- return self._newStoreObject.isSupportedComponent(componentType)
-
@inlineCallbacks
def iCalendarRolledup(self, request):
# FIXME: uncached: implement cache in the storage layer
@@ -1149,18 +1166,18 @@
Moving a calendar collection is allowed for the purposes of changing
that calendar's name.
"""
- defaultCalendar = (yield self.isDefaultCalendar(request))
+ defaultCalendarType = (yield self.isDefaultCalendar(request))
result = (yield super(CalendarCollectionResource, self).http_MOVE(request))
if result == NO_CONTENT:
destinationURI = urlsplit(request.headers.getHeader("destination"))[2]
destination = yield request.locateResource(destinationURI)
- yield self.movedCalendar(request, defaultCalendar,
+ yield self.movedCalendar(request, defaultCalendarType,
destination, destinationURI)
returnValue(result)
-class StoreScheduleInboxResource(_CommonHomeChildCollectionMixin, ScheduleInboxResource):
+class StoreScheduleInboxResource(_CalendarCollectionBehaviorMixin, _CommonHomeChildCollectionMixin, ScheduleInboxResource):
def __init__(self, *a, **kw):
@@ -1196,9 +1213,6 @@
def provision(self):
pass
- def isSupportedComponent(self, componentType):
- return self._newStoreObject.isSupportedComponent(componentType)
-
def http_DELETE(self, request):
return FORBIDDEN
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_props.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_props.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_props.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -74,7 +74,7 @@
if not supported_components:
self.fail("Expected CalDAV:supported-calendar-component-set element; but got none.")
- supported = set(("VEVENT", "VTODO", "VFREEBUSY"))
+ supported = set(("VEVENT",))
for component in supported_components.children:
if component.type in supported:
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_schedule.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_schedule.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_schedule.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -24,7 +24,7 @@
from twisted.internet.defer import inlineCallbacks
-from twistedcaldav import caldavxml
+from twistedcaldav import caldavxml, customxml
from twistedcaldav.test.util import HomeTestCase, TestCase
class Properties (HomeTestCase):
@@ -91,12 +91,11 @@
self.setupCalendars()
@inlineCallbacks
- def test_pick_default_calendar(self):
+ def test_pick_default_vevent_calendar(self):
"""
Make calendar
"""
-
request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
@@ -120,6 +119,34 @@
request._newStoreTransaction.abort()
@inlineCallbacks
+ def test_pick_default_vtodo_calendar(self):
+ """
+ Make calendar
+ """
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+ # default property initially not present
+ try:
+ inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
+ except HTTPError:
+ pass
+ else:
+ self.fail("customxml.ScheduleDefaultTasksURL is not empty")
+
+ yield inbox.pickNewDefaultCalendar(request, tasks=True)
+
+ try:
+ default = inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
+ except HTTPError:
+ self.fail("customxml.ScheduleDefaultTasksURL is not present")
+ else:
+ self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
+
+ request._newStoreTransaction.abort()
+
+ @inlineCallbacks
def test_pick_default_other(self):
"""
Make calendar
@@ -206,3 +233,71 @@
self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
request._newStoreTransaction.abort()
+
+ @inlineCallbacks
+ def test_set_default_vevent_other(self):
+ """
+ Test that the default URL can be set to another VEVENT calendar
+ """
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+ # default property not present
+ try:
+ inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
+ except HTTPError:
+ pass
+ else:
+ self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
+
+ # Create a new default calendar
+ newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+ yield newcalendar.createCalendarCollection()
+ yield newcalendar.setSupportedComponents(("VEVENT",))
+ request._newStoreTransaction.commit()
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+ yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")), request)
+
+ try:
+ default = inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
+ except HTTPError:
+ self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
+ else:
+ self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
+
+ request._newStoreTransaction.commit()
+
+ @inlineCallbacks
+ def test_is_default_calendar(self):
+ """
+ Test .isDefaultCalendar() returns the proper class or None.
+ """
+
+ # Create a new non-default calendar
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+ yield newcalendar.createCalendarCollection()
+ yield newcalendar.setSupportedComponents(("VEVENT",))
+ inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+ yield inbox.pickNewDefaultCalendar(request)
+ request._newStoreTransaction.commit()
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+ calendar = yield request.locateResource("/calendars/users/wsanchez/calendar")
+ newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+ tasks = yield request.locateResource("/calendars/users/wsanchez/tasks")
+
+ result = yield inbox.isDefaultCalendar(request, calendar)
+ self.assertEqual(result, caldavxml.ScheduleDefaultCalendarURL)
+
+ result = yield inbox.isDefaultCalendar(request, newcalendar)
+ self.assertEqual(result, None)
+
+ result = yield inbox.isDefaultCalendar(request, tasks)
+ self.assertEqual(result, customxml.ScheduleDefaultTasksURL)
+
+ request._newStoreTransaction.commit()
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_sharing.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_sharing.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -85,7 +85,7 @@
request = norequest()
result = yield super(SharingTests, self)._refreshRoot(request)
self.resource = (
- yield self.site.resource.locateChild(request, ["calendar"])
+ yield self.site.resource.locateChild(request, [config.CalDAV.AccountProvisioning.CalendarName])
)[0]
returnValue(result)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_validation.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_validation.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_validation.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -44,6 +44,7 @@
self.destination.name = lambda : '1'
self.destinationParent = CalDAVResource()
self.destinationParent.name = lambda : '2'
+ self.destinationParent.isSupportedComponent = lambda x: True
def _getSampleCalendar(self):
return Component.fromString("""BEGIN:VCALENDAR
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_wrapping.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/twistedcaldav/test/test_wrapping.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -121,7 +121,7 @@
pass
txn = self.calendarCollection._newStore.newTransaction()
home = yield txn.calendarHomeWithUID(uid, True)
- cal = yield home.calendarWithName("calendar")
+ cal = yield home.calendarWithName(config.CalDAV.AccountProvisioning.CalendarName)
yield cal.createCalendarObjectWithName(
objectName, VComponent.fromString(objectText)
)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/file.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/file.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -184,7 +184,7 @@
def createdHome(self):
# Default calendar
- defaultCal = self.createCalendarWithName("calendar")
+ defaultCal = self.createCalendarWithName(config.CalDAV.AccountProvisioning.CalendarName)
props = defaultCal.properties()
props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque())
@@ -268,6 +268,13 @@
result = str(self.properties().get(PropertyName.fromElement(customxml.TwistedCalendarSupportedComponents), ""))
return result if result else None
+ def isSupportedComponent(self, componentType):
+ supported = self.getSupportedComponents()
+ if supported:
+ return componentType.upper() in supported.split(",")
+ else:
+ return True
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/common.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/common.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -541,21 +541,22 @@
L{ICalendarHome.createdHome} creates a calendar only, or a calendar and tasks
collection only, in addition to inbox.
"""
+ self.patch(config.CalDAV.AccountProvisioning, "KeepComponentTypesSeparate", False)
home1 = yield self.transactionUnderTest().calendarHomeWithUID("home_provision1", create=True)
- for name in ("calendar", "inbox",):
+ for name in (config.CalDAV.AccountProvisioning.CalendarName, "inbox",):
calendar = yield home1.calendarWithName(name)
if calendar is None:
self.fail("calendar %r didn't exist" % (name,))
self.assertProvides(ICalendar, calendar)
self.assertEquals(calendar.name(), name)
- for name in ("tasks",):
+ for name in (config.CalDAV.AccountProvisioning.TasksName,):
calendar = yield home1.calendarWithName(name)
if calendar is not None:
self.fail("calendar %r exists" % (name,))
self.patch(config.CalDAV.AccountProvisioning, "KeepComponentTypesSeparate", True)
home2 = yield self.transactionUnderTest().calendarHomeWithUID("home_provision2", create=True)
- for name in ("calendar", "tasks", "inbox",):
+ for name in (config.CalDAV.AccountProvisioning.CalendarName, config.CalDAV.AccountProvisioning.TasksName, "inbox",):
calendar = yield home2.calendarWithName(name)
if calendar is None:
self.fail("calendar %r didn't exist" % (name,))
@@ -1314,7 +1315,7 @@
self.assertProvides(ICalendarHome, calendarHome)
# Default calendar should be automatically created.
self.assertProvides(ICalendar,
- (yield calendarHome.calendarWithName("calendar")))
+ (yield calendarHome.calendarWithName(config.CalDAV.AccountProvisioning.CalendarName)))
# A concurrent transaction shouldn't be able to read it yet:
self.assertIdentical((yield readOtherTxn()), None)
yield self.commit()
@@ -1935,9 +1936,9 @@
home2 = yield self.transactionUnderTest().calendarHomeWithUID(
"home2", create=True)
calendar1 = yield home1.calendarWithName("calendar_1")
- calendar2 = yield home2.calendarWithName("calendar")
+ calendar2 = yield home2.calendarWithName(config.CalDAV.AccountProvisioning.CalendarName)
objects = list(
- (yield (yield home2.calendarWithName("calendar")).calendarObjects()))
+ (yield (yield home2.calendarWithName(config.CalDAV.AccountProvisioning.CalendarName)).calendarObjects()))
self.assertEquals(objects, [])
for resourceName in self.requirements['home1']['calendar_1'].keys():
obj = yield calendar1.calendarObjectWithName(resourceName)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/util.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/util.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.config import config
"""
Utility logic common to multiple backend implementations.
@@ -276,7 +277,9 @@
(from a calendar in C{inHome}) and returns a L{VComponent} (to store in
a calendar in outHome).
"""
- yield outHome.removeCalendarWithName("calendar")
+ yield outHome.removeCalendarWithName(config.CalDAV.AccountProvisioning.CalendarName)
+ if config.CalDAV.AccountProvisioning.KeepComponentTypesSeparate:
+ yield outHome.removeCalendarWithName(config.CalDAV.AccountProvisioning.TasksName)
yield outHome.removeCalendarWithName("inbox")
outHome.properties().update(inHome.properties())
inCalendars = yield inHome.calendars()
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/test/util.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/test/util.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.config import config
"""
Store test utility functions
@@ -260,7 +261,9 @@
# We don't want the default calendar or inbox to appear unless it's
# explicitly listed.
try:
- yield home.removeCalendarWithName("calendar")
+ yield home.removeCalendarWithName(config.CalDAV.AccountProvisioning.CalendarName)
+ if config.CalDAV.AccountProvisioning.KeepComponentTypesSeparate:
+ yield home.removeCalendarWithName(config.CalDAV.AccountProvisioning.TasksName)
yield home.removeCalendarWithName("inbox")
except NoSuchHomeChildError:
pass
@@ -496,7 +499,6 @@
# else in this module; nothing else in this module should ever touch global
# configuration. -glyph
- from twistedcaldav.config import config
from twistedcaldav.memcacher import Memcacher
aTest.patch(config.Memcached.Pools.Default, "ClientEnabled", False)
Modified: CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/upgrade/test/test_migrate.py
===================================================================
--- CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/upgrade/test/test_migrate.py 2011-10-20 17:06:21 UTC (rev 8213)
+++ CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/upgrade/test/test_migrate.py 2011-10-20 17:45:22 UTC (rev 8214)
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.config import config
"""
Tests for L{txdav.common.datastore.upgrade.migrate}.
@@ -129,7 +130,7 @@
self.addCleanup(vrfyTxn.commit)
home = yield vrfyTxn.calendarHomeWithUID("home1")
# The default calendar is still there.
- self.assertNotIdentical(None, (yield home.calendarWithName("calendar")))
+ self.assertNotIdentical(None, (yield home.calendarWithName(config.CalDAV.AccountProvisioning.CalendarName)))
# The migrated calendar isn't.
self.assertIdentical(None, (yield home.calendarWithName("calendar_1")))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111020/4cace239/attachment-0001.html>
More information about the calendarserver-changes
mailing list