[CalendarServer-changes] [9392] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Wed Jun 27 10:17:03 PDT 2012
Revision: 9392
http://trac.macosforge.org/projects/calendarserver/changeset/9392
Author: cdaboo at apple.com
Date: 2012-06-27 10:17:03 -0700 (Wed, 27 Jun 2012)
Log Message:
-----------
Supported extended free busy responses.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/caldavxml.py
CalendarServer/trunk/twistedcaldav/directory/principal.py
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/method/report_common.py
CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
Modified: CalendarServer/trunk/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/caldavxml.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/caldavxml.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -26,6 +26,7 @@
"""
from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
from txdav.xml.element import registerElement, dav_namespace
from txdav.xml.element import WebDAVElement, PCDATAElement
@@ -140,6 +141,21 @@
"""
return iComponent.fromString(str(self))
+ def gettimezone(self):
+ """
+ Get the timezone to use. If none, return UTC timezone.
+
+ @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
+ """
+ calendar = self.calendar()
+ if calendar is not None:
+ tz = calendar.gettimezone()
+ if tz is not None:
+ return tz
+
+ # Default to using utc tzinfo
+ return PyCalendarTimezone(utc=True)
+
def valid(self):
"""
Determine whether the content of this element is a valid single VTIMEZONE component.
Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -724,6 +724,9 @@
def __ne__(self, other):
return not self.__eq__(other)
+ def __hash__(self):
+ return hash(self.principalUID())
+
@inlineCallbacks
def readProperty(self, property, request):
if type(property) is tuple:
@@ -816,6 +819,23 @@
return self.principalURL()
@inlineCallbacks
+ def isProxyFor(self, principal):
+ """
+ Determine whether this principal is a read-only or read-write proxy for the
+ specified principal.
+ """
+
+ read_uids = (yield self.proxyFor(False))
+ if principal in read_uids:
+ returnValue(True)
+
+ write_uids = (yield self.proxyFor(True))
+ if principal in write_uids:
+ returnValue(True)
+
+ returnValue(False)
+
+ @inlineCallbacks
def proxyFor(self, read_write, resolve_memberships=True):
proxyFors = set()
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -960,11 +960,14 @@
@param start: the L{PyCalendarDateTime} for the start of the range.
@param end: the L{PyCalendarDateTime} for the end of the range.
- @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day.
+ @param timezone: the L{Component} or L{PyCalendarTimezone} of the VTIMEZONE to use for floating/all-day.
@return: the L{Component} for the new calendar with expanded instances.
"""
- pytz = PyCalendarTimezone(tzid=timezone.propertyValue("TZID")) if timezone else None
+ if timezone is not None and isinstance(timezone, Component):
+ pytz = PyCalendarTimezone(tzid=timezone.propertyValue("TZID"))
+ else:
+ pytz = timezone
# Create new calendar object with same properties as the original, but
# none of the originals sub-components
@@ -1922,6 +1925,27 @@
return None
+ def getExtendedFreeBusy(self):
+ """
+ Get the X-CALENDARSEREVR-EXTENDED-FREEBUSY value. Works on either a VCALENDAR or on a component.
+
+ @return: the string value of the X-CALENDARSEREVR-EXTENDED-FREEBUSY property, or None
+ """
+
+ # Extract appropriate sub-component if this is a VCALENDAR
+ if self.name() == "VCALENDAR":
+ for component in self.subcomponents():
+ if component.name() not in ignoredComponents:
+ return component.getExtendedFreeBusy()
+ else:
+ try:
+ # Find the primary subcomponent
+ return self.propertyValue("X-CALENDARSEREVR-EXTENDED-FREEBUSY")
+ except InvalidICalendarDataError:
+ pass
+
+ return None
+
def setParameterToValueForPropertyWithValue(self, paramname, paramvalue, propname, propvalue):
"""
Add or change the parameter to the specified value on the property having the specified value.
@@ -2269,7 +2293,7 @@
def removeXComponents(self, keep_components=()):
"""
- Remove all X- properties except the specified ones
+ Remove all X- components except the specified ones
"""
for component in tuple(self.subcomponents()):
Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -431,10 +431,19 @@
yield fbcacher.set(key, entry)
@inlineCallbacks
-def generateFreeBusyInfo(request, calresource, fbinfo, timerange, matchtotal,
- excludeuid=None, organizer=None,
- organizerPrincipal=None, same_calendar_user=False,
- servertoserver=False):
+def generateFreeBusyInfo(
+ request,
+ calresource,
+ fbinfo,
+ timerange,
+ matchtotal,
+ excludeuid=None,
+ organizer=None,
+ organizerPrincipal=None,
+ same_calendar_user=False,
+ servertoserver=False,
+ event_details=None,
+):
"""
Run a free busy report on the specified calendar collection
accumulating the free busy info for later processing.
@@ -453,6 +462,7 @@
being targeted.
@param servertoserver: a C{bool} indicating whether we are doing a local or
remote lookup request.
+ @param event_details: a C{list} into which to store extended VEVENT details if not C{None}
"""
# First check the privilege on this collection
@@ -473,7 +483,7 @@
useruid = userPrincipal.principalUID()
else:
useruid = ""
-
+
# Get the timezone property from the collection.
has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
if has_prop:
@@ -481,6 +491,13 @@
else:
tz = None
+ # Look for possible extended free busy information
+ do_event_details = False
+ if event_details is not None and organizer_principal is not None and userPrincipal is not None:
+
+ # Check of organizer is a delegate of attendee
+ do_event_details = (yield organizer_principal.isProxyFor(userPrincipal))
+
# Try cache
resources = (yield FBCacheEntry.getCacheEntry(calresource, useruid, timerange)) if config.EnableFreeBusyCache else None
@@ -537,19 +554,8 @@
request.extendedLogItems["fb-cached"] = request.extendedLogItems.get("fb-cached", 0) + 1
# Determine appropriate timezone (UTC is the default)
- tzinfo = None
- if tz is not None:
- calendar = tz.calendar()
- if calendar is not None:
- for subcomponent in calendar.subcomponents():
- if subcomponent.name() == "VTIMEZONE":
- # <filter> contains exactly one <comp-filter>
- tzinfo = subcomponent.gettimezone()
- break
+ tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
- if tzinfo is None:
- tzinfo = PyCalendarTimezone(utc=True)
-
# We care about separate instances for VEVENTs only
aggregated_resources = {}
for name, uid, type, test_organizer, float, start, end, fbtype, transp in resources:
@@ -612,6 +618,12 @@
if matchtotal > max_number_of_matches:
raise NumberOfMatchesWithinLimits(max_number_of_matches)
+ # Add extended details
+ if do_event_details:
+ child = (yield request.locateChildResource(calresource, name))
+ calendar = (yield child.iCalendarForUser(request))
+ _addEventDetails(calendar, event_details, timerange, tzinfo)
+
else:
child = (yield request.locateChildResource(calresource, name))
calendar = (yield child.iCalendarForUser(request))
@@ -652,9 +664,53 @@
processAvailabilityFreeBusy(calendar, fbinfo, timerange)
else:
assert "Free-busy query returned unwanted component: %s in %r", (name, calresource,)
+
+ # Add extended details
+ if calendar.mainType() == "VEVENT" and do_event_details:
+ child = (yield request.locateChildResource(calresource, name))
+ calendar = (yield child.iCalendarForUser(request))
+ _addEventDetails(calendar, event_details, timerange, tzinfo)
returnValue(matchtotal)
+def _addEventDetails(calendar, event_details, timerange, tzinfo):
+ """
+ Expand events within the specified time range and limit the set of properties to those allowed for
+ delegate extended free busy.
+
+ @param calendar: the calendar object to expand
+ @type calendar: L{Component}
+ @param event_details: list to append VEVENT components to
+ @type event_details: C{list}
+ @param timerange: the time-range in which to expand
+ @type timerange: L{TimeRange}
+ @param tzinfo: timezone for floating time calculations
+ @type tzinfo: L{PyCalendarTimezone}
+ """
+
+ # First expand the component
+ expanded = calendar.expand(timerange.start, timerange.end, timezone=tzinfo)
+
+ # Remove all but essential properties
+ expanded.filterProperties(keep=(
+ "UID",
+ "RECURRENCE-ID",
+ "DTSTAMP",
+ "DTSTART",
+ "DTEND",
+ "DURATION",
+ "SUMMARY",
+ ))
+
+ # Need to remove all child components of VEVENT
+ for subcomponent in expanded.subcomponents():
+ if subcomponent.name() == "VEVENT":
+ for sub in tuple(subcomponent.subcomponents()):
+ subcomponent.removeComponent(sub)
+
+ event_details.extend([subcomponent for subcomponent in expanded.subcomponents() if subcomponent.name() == "VEVENT"])
+
+
def processEventFreeBusy(calendar, fbinfo, timerange, tzinfo):
"""
Extract free busy data from a VEVENT component.
@@ -838,17 +894,19 @@
normalizePeriodList(periods)
return periods
-def buildFreeBusyResult(fbinfo, timerange, organizer=None, attendee=None, uid=None, method=None):
+def buildFreeBusyResult(fbinfo, timerange, organizer=None, attendee=None, uid=None, method=None, event_details=None):
"""
Generate a VCALENDAR object containing a single VFREEBUSY that is the
aggregate of the free busy info passed in.
- @param fbinfo: the array of busy periods to use.
- @param timerange: the L{TimeRange} for the query.
- @param organizer: the L{Property} for the Organizer of the free busy request, or None.
- @param attendee: the L{Property} for the Attendee responding to the free busy request, or None.
- @param uid: the UID value from the free busy request.
- @param method: the METHOD property value to insert.
- @return: the L{Component} containing the calendar data.
+
+ @param fbinfo: the array of busy periods to use.
+ @param timerange: the L{TimeRange} for the query.
+ @param organizer: the L{Property} for the Organizer of the free busy request, or None.
+ @param attendee: the L{Property} for the Attendee responding to the free busy request, or None.
+ @param uid: the UID value from the free busy request.
+ @param method: the METHOD property value to insert.
+ @param event_details: VEVENT components to add.
+ @return: the L{Component} containing the calendar data.
"""
# Merge overlapping time ranges in each fb info section
@@ -883,4 +941,8 @@
uid = md5(str(fbcalendar) + str(time.time())).hexdigest()
fb.addProperty(Property("UID", uid))
+ if event_details:
+ for vevent in event_details:
+ fbcalendar.addComponent(vevent)
+
return fbcalendar
Modified: CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -115,21 +115,10 @@
"""
assert tzelement is None or isinstance(tzelement, CalDAVTimeZoneElement)
- if tzelement is not None:
- calendar = tzelement.calendar()
- if calendar is not None:
- for subcomponent in calendar.subcomponents():
- if subcomponent.name() == "VTIMEZONE":
- # <filter> contains exactly one <comp-filter>
- tz = subcomponent.gettimezone()
- self.child.settzinfo(tz)
- return tz
+ tz = tzelement.gettimezone() if tzelement is not None else PyCalendarTimezone(utc=True)
+ self.child.settzinfo(tz)
+ return tz
- # Default to using utc tzinfo
- utc = PyCalendarTimezone(utc=True)
- self.child.settzinfo(utc)
- return utc
-
def getmaxtimerange(self):
"""
Get the date farthest into the future in any time-range elements
Modified: CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/caldav.py 2012-06-27 17:10:17 UTC (rev 9391)
+++ CalendarServer/trunk/twistedcaldav/scheduling/caldav.py 2012-06-27 17:17:03 UTC (rev 9392)
@@ -131,7 +131,10 @@
# Different behavior for free-busy vs regular invite
if self.freebusy:
- yield self.generateFreeBusyResponse(recipient, self.responses, organizerProp, organizerPrincipal, uid)
+ # Look for special delegate extended free-busy request
+ event_details = [] if self.scheduler.calendar.getExtendedFreeBusy() else None
+
+ yield self.generateFreeBusyResponse(recipient, self.responses, organizerProp, organizerPrincipal, uid, event_details)
else:
yield self.generateResponse(recipient, self.responses)
@@ -206,7 +209,7 @@
returnValue(True)
@inlineCallbacks
- def generateFreeBusyResponse(self, recipient, responses, organizerProp, organizerPrincipal, uid):
+ def generateFreeBusyResponse(self, recipient, responses, organizerProp, organizerPrincipal, uid, event_details):
# Extract the ATTENDEE property matching current recipient from the calendar data
cuas = recipient.principal.calendarUserAddresses()
@@ -222,6 +225,7 @@
uid,
attendeeProp,
remote,
+ event_details,
))
except:
log.err("Could not determine free busy information: %s" % (recipient.cuaddr,))
@@ -246,7 +250,7 @@
returnValue(True)
@inlineCallbacks
- def generateAttendeeFreeBusyResponse(self, recipient, organizerProp, organizerPrincipal, uid, attendeeProp, remote):
+ def generateAttendeeFreeBusyResponse(self, recipient, organizerProp, organizerPrincipal, uid, attendeeProp, remote, event_details=None):
# Find the current recipients calendar-free-busy-set
fbset = (yield recipient.principal.calendarFreeBusyURIs(self.scheduler.request))
@@ -290,6 +294,7 @@
organizerPrincipal = organizerPrincipal,
same_calendar_user = same_calendar_user,
servertoserver=remote,
+ event_details=event_details,
))
# Build VFREEBUSY iTIP reply for this recipient
@@ -299,7 +304,8 @@
organizer = organizerProp,
attendee = attendeeProp,
uid = uid,
- method = "REPLY"
+ method = "REPLY",
+ event_details=event_details,
)
returnValue(fbresult)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120627/60c5f964/attachment-0001.html>
More information about the calendarserver-changes
mailing list