[CalendarServer-changes] [2779] CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Thu Aug 7 07:24:31 PDT 2008
Revision: 2779
http://trac.macosforge.org/projects/calendarserver/changeset/2779
Author: cdaboo at apple.com
Date: 2008-08-07 07:24:30 -0700 (Thu, 07 Aug 2008)
Log Message:
-----------
Handle implicit scheduling deletes. This is not incomplete - need a transaction based model to ensure
both the file delete and iTIP scheduling succeed before the delete is actually done.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/delete.py
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/put_common.py
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/implicit.py
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/test/test_icaldiff.py
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/delete.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/delete.py 2008-08-06 22:15:42 UTC (rev 2778)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/delete.py 2008-08-07 14:24:30 UTC (rev 2779)
@@ -20,35 +20,42 @@
__all__ = ["http_DELETE"]
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.web2 import responsecode
from twisted.web2.dav.util import parentForURL
from twistedcaldav.resource import isPseudoCalendarCollectionResource
+from twistedcaldav.scheduling.implicit import ImplicitScheduler
+ at inlineCallbacks
def http_DELETE(self, request):
#
# Override base DELETE request handling to ensure that the calendar
# index file has the entry for the deleted calendar component removed.
#
- def gotParent(parent):
- def gotResponse(response):
- if response == responsecode.NO_CONTENT:
- if isPseudoCalendarCollectionResource(parent):
- index = parent.index()
- index.deleteResource(self.fp.basename())
- # Change CTag on the parent calendar collection
- d = parent.updateCTag()
- d.addCallback(lambda _: response)
- return d
+ # TODO: need to use transaction based delete on live scheduling object resources
+ # as the iTIP operation may fail and may need to prevent the delete from happening.
- return response
+ parentURL = parentForURL(request.uri)
+ parent = (yield request.locateResource(parentURL))
- d = super(CalDAVFile, self).http_DELETE(request)
- d.addCallback(gotResponse)
- return d
+ if isPseudoCalendarCollectionResource(parent) and self.exists():
+ calendar = self.iCalendar()
- parentURL = parentForURL(request.uri)
- d = request.locateResource(parentURL)
- d.addCallback(gotParent)
- return d
+ response = (yield super(CalDAVFile, self).http_DELETE(request))
+
+ if response == responsecode.NO_CONTENT:
+ if isPseudoCalendarCollectionResource(parent):
+
+ index = parent.index()
+ index.deleteResource(self.fp.basename())
+
+ # Change CTag on the parent calendar collection
+ yield parent.updateCTag()
+
+ # Do scheduling
+ scheduler = ImplicitScheduler()
+ yield scheduler.doImplicitScheduling(request, self, calendar, True)
+
+ returnValue(response)
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/put_common.py 2008-08-06 22:15:42 UTC (rev 2778)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/method/put_common.py 2008-08-07 14:24:30 UTC (rev 2779)
@@ -679,7 +679,7 @@
# Do scheduling
if not self.isiTIP:
scheduler = ImplicitScheduler()
- self.calendar = (yield scheduler.doImplicitScheduling(self.request, self.destination, self.calendar))
+ self.calendar = (yield scheduler.doImplicitScheduling(self.request, self.destination, self.calendar, False))
# Initialize the rollback system
self.setupRollback()
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/implicit.py 2008-08-06 22:15:42 UTC (rev 2778)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/implicit.py 2008-08-07 14:24:30 UTC (rev 2779)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.web2 import responsecode
from twisted.web2.dav.http import ErrorResponse
from twisted.web2.http import HTTPError
@@ -22,6 +22,8 @@
from twistedcaldav.itip import iTipGenerator
from twistedcaldav.log import Logger
from twistedcaldav.scheduling.scheduler import CalDAVScheduler
+from twistedcaldav.method import report_common
+from twistedcaldav.scheduling.icaldiff import iCalDiff
__all__ = [
"ImplicitScheduler",
@@ -29,13 +31,19 @@
log = Logger()
+# TODO:
+#
+# Handle the case where a PUT removes the ORGANIZER property. That should be equivalent to cancelling the entire meeting.
+#
+# Figure out proper behavior for what happens when an attendee deletes their copy of an event. For now disallow.
+
class ImplicitScheduler(object):
def __init__(self):
pass
@inlineCallbacks
- def doImplicitScheduling(self, request, resource, calendar):
+ def doImplicitScheduling(self, request, resource, calendar, deleting):
"""
Do implicit scheduling operation based on the calendar data that is being PUT
@@ -43,9 +51,11 @@
@type request:
@param resource:
@type resource:
- @param calendar:
- @type calendar:
-
+ @param calendar: the calendar data being written, or None if deleting
+ @type calendar: L{Component} or C{None}
+ @param deleting: C{True} if the resource is being deleting
+ @type deleting: bool
+
@return: a new calendar object modified with scheduling information
"""
@@ -53,8 +63,12 @@
self.resource = resource
self.calendar = calendar
self.calendar_owner = (yield self.resource.owner(self.request))
+ self.deleting = deleting
-
+ # When deleting we MUST have the calendar as the actual resource
+ # will have been deleted by now
+ assert deleting and calendar or not deleting
+
# Get some useful information from the calendar
self.extractCalendarData()
@@ -75,6 +89,7 @@
if self.organizer:
if organizer != self.organizer:
# We have different ORGANIZERs in the same iCalendar object - this is an error
+ log.error("Only one ORGANIZER is allowed in an iCalendar object:\n%s" % (self.calendar,))
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "single-organizer")))
else:
self.organizer = organizer
@@ -84,6 +99,9 @@
self.attendees = set()
for attendee, _ignore in self.attendeesByInstance:
self.attendees.add(attendee)
+
+ # Some other useful things
+ self.uid = self.calendar.resourceUID()
def isOrganizerScheduling(self):
"""
@@ -124,29 +142,42 @@
@inlineCallbacks
def doImplicitOrganizer(self):
+ # Check for a delete
+ if self.deleting:
+
+ log.debug("Implicit - organizer '%s' is deleting UID: '%s'" % (self.organizer, self.uid))
+ self.oldcalendar = self.calendar
+
+ # Cancel all attendees
+ self.cancelledAttendees = [(attendee, None) for attendee in self.attendees]
+
# Check for a new resource or an update
- if self.resource.exists():
-
+ elif self.resource.exists():
+
# Read in existing data
self.oldcalendar = self.resource.iCalendar()
# Significant change
- if not self.isChangeSignificant():
+ if self.isChangeInsignificant():
# Nothing to do
+ log.debug("Implicit - organizer '%s' is updating UID: '%s' but change is not significant" % (self.organizer, self.uid))
return
+ log.debug("Implicit - organizer '%s' is updating UID: '%s'" % (self.organizer, self.uid))
+
# Check for removed attendees
self.findRemovedAttendees()
else:
+ log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
self.oldcalendar = None
self.cancelledAttendees = ()
yield self.scheduleWithAttendees()
- def isChangeSignificant(self):
+ def isChangeInsignificant(self):
- # TODO: diff two calendars and see what happened. For now treat any change as significant.
- return True
+ differ = iCalDiff(self.oldcalendar, self.calendar)
+ return differ.organizerDiff()
def findRemovedAttendees(self):
"""
@@ -168,7 +199,8 @@
yield self.processCancels()
# Process regular requests next
- yield self.processRequests()
+ if not self.deleting:
+ yield self.processRequests()
@inlineCallbacks
def processCancels(self):
@@ -203,11 +235,10 @@
scheduler = CalDAVScheduler(self.request, self.resource)
# Do the PUT processing
+ log.info("Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'" % (self.organizer, attendee, self.uid, rids))
response = (yield scheduler.doSchedulingViaPUT(self.organizer, (attendee,), itipmsg))
+ self.handleSchedulingResponse(response, True)
- # TODO: need to figure out how to process the response for a CANCEL
- returnValue(response)
-
@inlineCallbacks
def processRequests(self):
@@ -229,23 +260,35 @@
scheduler = CalDAVScheduler(self.request, self.resource)
# Do the PUT processing
+ log.info("Implicit REQUEST - organizer: '%s' to attendee: '%s', UID: '%s'" % (self.organizer, attendee, self.uid,))
response = (yield scheduler.doSchedulingViaPUT(self.organizer, (attendee,), itipmsg))
-
- # TODO: need to figure out how to process the response for a REQUEST
- returnValue(response)
-
+ self.handleSchedulingResponse(response, True)
+
+ def handleSchedulingResponse(self, response, is_organizer):
+
+ # TODO: need to figure out how to process the response
+ pass
+
@inlineCallbacks
def doImplicitAttendee(self):
+ if self.deleting:
+ log.error("Attendee '%s' is not allowed to delete an organized event: UID:%s" % (self.attendeePrincipal, self.uid,))
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-attendee-change")))
+
# Get the ORGANIZER's current copy of the calendar object
- self.orgcalendar = self.getOrganizersCopy()
+ yield self.getOrganizersCopy()
+ assert self.organizer_calendar, "Must have the organizer's copy of an invite"
# Determine whether the current change is allowed
- if not self.isAttendeeChangeSignificant():
+ if self.isAttendeeChangeInsignificant():
+ log.debug("Implicit - attendee '%s' is updating UID: '%s' but change is not significant" % (self.attendee, self.uid))
return
+ log.debug("Implicit - attendee '%s' is updating UID: '%s'" % (self.attendee, self.uid))
yield self.scheduleWithOrganizer()
+ @inlineCallbacks
def getOrganizersCopy(self):
"""
Get the Organizer's copy of the event being processed.
@@ -255,20 +298,44 @@
the attendee does the right thing about changing the details in the event.
"""
- # TODO: extract UID and ORGANIZER, find match in ORGANIZER's calendars.
+ self.organizer_calendar = None
+ if self.organizerPrincipal:
+ # Get Organizer's calendar-home
+ calendar_home = self.organizerPrincipal.calendarHome()
+
+ # FIXME: because of the URL->resource request mapping thing, we have to force the request
+ # to recognize this resource
+ self.request._rememberResource(calendar_home, calendar_home.url())
+
+ # Run a UID query against the UID
- return None
+ def queryCalendarCollection(collection, uri):
+ rname = collection.index().resourceNameForUID(self.uid)
+ if rname:
+ self.organizer_calendar = collection.iCalendar(rname)
+ return succeed(False)
+ else:
+ return succeed(True)
+
+ # NB We are by-passing privilege checking here. That should be OK as the data found is not
+ # exposed to the user.
+ yield report_common.applyToCalendarCollections(calendar_home, self.request, calendar_home.url(), "infinity", queryCalendarCollection, None)
- def isAttendeeChangeSignificant(self):
+ def isAttendeeChangeInsignificant(self):
"""
Check whether the change is significant (PARTSTAT) or allowed
(attendee can only change their property, alarms, TRANSP, and
instances. Raise an exception if it is not allowed.
"""
- # TODO: all of the above.
- return True
+ differ = iCalDiff(self.organizer_calendar, self.calendar)
+ change_allowed, no_itip = differ.attendeeMerge(self.attendee)
+ if not change_allowed:
+ log.error("Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s" % (self.attendeePrincipal, self.uid,))
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-attendee-change")))
+ return no_itip
+
@inlineCallbacks
def scheduleWithOrganizer(self):
@@ -280,7 +347,6 @@
scheduler = CalDAVScheduler(self.request, self.resource)
# Do the PUT processing
+ log.info("Implicit REPLY - attendee: '%s' to organizer: '%s', UID: '%s'" % (self.attendee, self.organizer, self.uid,))
response = (yield scheduler.doSchedulingViaPUT(self.attendee, (self.organizer,), itipmsg))
-
- # TODO: need to figure out how to process the response for a REQUEST
- returnValue(response)
+ self.handleSchedulingResponse(response, False)
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/test/test_icaldiff.py 2008-08-06 22:15:42 UTC (rev 2778)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/scheduling/test/test_icaldiff.py 2008-08-07 14:24:30 UTC (rev 2779)
@@ -15,7 +15,7 @@
##
from twistedcaldav.scheduling.icaldiff import iCalDiff
-from twistedcaldav.ical import Property, Component
+from twistedcaldav.ical import Component
import twistedcaldav.test.util
class ICalDiff (twistedcaldav.test.util.TestCase):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080807/ce7a17e5/attachment-0001.html
More information about the calendarserver-changes
mailing list