[CalendarServer-changes] [6579] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Nov 9 08:36:37 PST 2010
Revision: 6579
http://trac.macosforge.org/projects/calendarserver/changeset/6579
Author: cdaboo at apple.com
Date: 2010-11-09 08:36:32 -0800 (Tue, 09 Nov 2010)
Log Message:
-----------
Get rid of remaining Proto* classes from storebridge. Do some more re-factoring of common code in storebridge.
Re-factor some code in the new store.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
CalendarServer/trunk/twistedcaldav/method/report_common.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/carddav/datastore/file.py
CalendarServer/trunk/txdav/carddav/datastore/sql.py
CalendarServer/trunk/txdav/carddav/datastore/test/common.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
Modified: CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -231,10 +231,9 @@
)
for child, child_uri in ok_resources:
child_uri_name = child_uri[child_uri.rfind("/") + 1:]
- child_path_name = urllib.unquote(child_uri_name)
if generate_address_data or not index_query_ok:
- vcard = yield addrresource.vCard(child_path_name)
+ vcard = yield child.vCard()
assert vcard is not None, "vCard %s is missing from address book collection %r" % (child_uri_name, self)
else:
vcard = None
Modified: CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -20,8 +20,6 @@
__all__ = ["report_urn_ietf_params_xml_ns_caldav_calendar_query"]
-import urllib
-
from twext.python.log import Logger
from twext.web2.dav.http import ErrorResponse
@@ -195,10 +193,9 @@
for child, child_uri in ok_resources:
child_uri_name = child_uri[child_uri.rfind("/") + 1:]
- child_path_name = urllib.unquote(child_uri_name)
if generate_calendar_data or not index_query_ok:
- calendar = (yield calresource.iCalendarForUser(request, child_path_name))
+ calendar = (yield child.iCalendarForUser(request))
assert calendar is not None, "Calendar %s is missing from calendar collection %r" % (child_uri_name, self)
else:
calendar = None
Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -533,7 +533,7 @@
raise NumberOfMatchesWithinLimits(max_number_of_matches)
else:
- calendar = (yield calresource.iCalendarForUser(request, name))
+ calendar = (yield child.iCalendarForUser(request))
# The calendar may come back as None if the resource is being changed, or was deleted
# between our initial index query and getting here. For now we will ignore this error, but in
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -1081,14 +1081,7 @@
returnValue(False)
@inlineCallbacks
- def iCalendarForUser(self, request, name=None):
- if name is not None:
- # FIXME: this is really the caller's job; why am I looking up sub-
- # resources?
- returnValue(
- (yield (yield request.locateChildResource(self, name)
- ).iCalendarForUser(request))
- )
+ def iCalendarForUser(self, request):
caldata = yield self.iCalendar()
@@ -1131,7 +1124,7 @@
@inlineCallbacks
- def vCard(self, name=None):
+ def vCard(self):
"""
See L{ICalDAVResource.vCard}.
@@ -1143,7 +1136,7 @@
methods.
"""
try:
- vcard_data = yield self.vCardText(name)
+ vcard_data = yield self.vCardText()
except InternalDataStoreError:
returnValue(None)
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -28,7 +28,6 @@
from twisted.python.log import err as logDefaultException
from twisted.python.util import FancyEqMixin
-from twext.python import vcomponent
from twext.python.log import Logger
from twext.web2 import responsecode
@@ -54,6 +53,8 @@
from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource
from twistedcaldav.schedule import ScheduleInboxResource
from twistedcaldav.scheduling.implicit import ImplicitScheduler
+from twistedcaldav.ical import Component as VCalendar
+from twistedcaldav.ical import Property as VProperty
from twistedcaldav.vcard import Component as VCard
from txdav.base.propertystore.base import PropertyName
@@ -157,8 +158,11 @@
class _NewStoreFileMetaDataHelper(object):
+ def exists(self):
+ return self._newStoreObject is not None
+
def name(self):
- return self._newStoreObject.name() if self._newStoreObject is not None else None
+ return self._newStoreObject.name() if self._newStoreObject is not None else self._name
def etag(self):
@@ -192,7 +196,6 @@
"""
_childClass = None
- _protoChildClass = None
def _initializeWithHomeChild(self, child, home):
"""
@@ -244,23 +247,18 @@
@inlineCallbacks
def makeChild(self, name):
"""
- Create a L{CalendarObjectResource} or L{ProtoCalendarObjectResource}
- based on a calendar object name.
+ Create a L{CalendarObjectResource} based on a calendar object name.
"""
if self._newStoreObject:
newStoreObject = yield self._newStoreObject.objectResourceWithName(name)
- if newStoreObject is not None:
- similar = self._childClass(
- newStoreObject,
- principalCollections=self._principalCollections
- )
- else:
- similar = self._protoChildClass(
- self._newStoreObject, name,
- principalCollections=self._principalCollections
- )
+ similar = self._childClass(
+ newStoreObject,
+ self._newStoreObject,
+ name,
+ principalCollections=self._principalCollections
+ )
self.propagateTransaction(similar)
returnValue(similar)
@@ -395,6 +393,9 @@
# Actually delete it.
yield self._newStoreObject.remove()
+
+ # Re-initialize to get stuff setup again now we have no object
+ self._initializeWithHomeChild(None, self._newStoreParentHome)
# FIXME: handle exceptions, possibly like this:
@@ -457,7 +458,6 @@
"""
self._childClass = CalendarObjectResource
- self._protoChildClass = ProtoCalendarObjectResource
super(CalendarCollectionResource, self).__init__(*args, **kw)
self._initializeWithHomeChild(calendar, home)
self._name = calendar.name() if calendar else name
@@ -487,8 +487,8 @@
# FIXME: uncached: implement cache in the storage layer
# Generate a monolithic calendar
- calendar = vcomponent.VComponent("VCALENDAR")
- calendar.addProperty(vcomponent.VProperty("VERSION", "2.0"))
+ calendar = VCalendar("VCALENDAR")
+ calendar.addProperty(VProperty("VERSION", "2.0"))
# Do some optimisation of access control calculation by determining any
# inherited ACLs outside of the child resource loop and supply those to
@@ -515,7 +515,7 @@
# Get the access filtered view of the data
caldata = yield child.iCalendarTextFiltered(isowner, accessPrincipal.principalUID() if accessPrincipal else "")
try:
- subcalendar = vcomponent.VComponent.fromString(caldata)
+ subcalendar = VCalendar.fromString(caldata)
except ValueError:
continue
assert subcalendar.name() == "VCALENDAR"
@@ -610,7 +610,6 @@
def __init__(self, *a, **kw):
self._childClass = CalendarObjectResource
- self._protoChildClass = ProtoCalendarObjectResource
super(StoreScheduleInboxResource, self).__init__(*a, **kw)
self.parent.propagateTransaction(self)
@@ -778,15 +777,12 @@
@inlineCallbacks
def getChild(self, name):
attachment = yield self._newStoreCalendarObject.attachmentWithName(name)
- if attachment is None:
- result = ProtoCalendarAttachment(
- self._newStoreCalendarObject,
- name,
- principalCollections=self.principalCollections())
- else:
- result = CalendarAttachment(
- self._newStoreCalendarObject,
- attachment, principalCollections=self.principalCollections())
+ result = CalendarAttachment(
+ self._newStoreCalendarObject,
+ attachment,
+ name,
+ principalCollections=self.principalCollections()
+ )
self.propagateTransaction(result)
returnValue(result)
@@ -900,94 +896,54 @@
-class ProtoCalendarAttachment(_NewStoreFileMetaDataHelper, _GetChildHelper):
-
- def __init__(self, calendarObject, attachmentName, **kw):
- super(ProtoCalendarAttachment, self).__init__(**kw)
- self.calendarObject = calendarObject
- self.attachmentName = attachmentName
- self._newStoreObject = None
-
-
- def isCollection(self):
- return False
-
-
- def http_DELETE(self, request):
- return NO_CONTENT
-
-
- # FIXME: Permissions should dictate a different response, sometimes.
- def http_GET(self, request):
- return NOT_FOUND
-
-
- @requiresPermissions(fromParent=[davxml.Bind()])
- @inlineCallbacks
- def http_PUT(self, request):
- # FIXME: direct test
- # FIXME: transformation?
- content_type = request.headers.getHeader("content-type")
- if content_type is None:
- content_type = MimeType("application", "octet-stream")
- t = yield self.calendarObject.createAttachmentWithName(
- self.attachmentName,
- content_type,
- )
- yield readStream(request.stream, t.write)
- self._newStoreObject = yield self.calendarObject.attachmentWithName(
- self.attachmentName
- )
- yield t.loseConnection()
- returnValue(CREATED)
-
- http_MKCOL = None
- http_MKCALENDAR = None
-
-
-
class CalendarAttachment(_NewStoreFileMetaDataHelper, _GetChildHelper):
- def __init__(self, calendarObject, attachment, **kw):
+ def __init__(self, calendarObject, attachment, attachmentName, **kw):
super(CalendarAttachment, self).__init__(**kw)
self._newStoreCalendarObject = calendarObject
self._newStoreAttachment = self._newStoreObject = attachment
+ self.attachmentName = attachmentName
- def etag(self):
- # FIXME: test
- return ETag(self._newStoreAttachment.md5())
-
-
- def contentType(self):
- # FIXME: test
- return self._newStoreAttachment.contentType()
-
-
def getChild(self, name):
return None
@requiresPermissions(davxml.WriteContent())
+ @inlineCallbacks
def http_PUT(self, request):
# FIXME: direct test
- # FIXME: refactor with ProtoCalendarAttachment.http_PUT
# FIXME: CDT test to make sure that permissions are enforced.
content_type = request.headers.getHeader("content-type")
if content_type is None:
content_type = MimeType("application", "octet-stream")
- t = self._newStoreAttachment.store(content_type)
- @inlineCallbacks
- def done(ignored):
+ if self._newStoreAttachment:
+ t = self._newStoreAttachment.store(content_type)
+ yield readStream(request.stream, t.write)
yield t.loseConnection()
returnValue(NO_CONTENT)
- return readStream(request.stream, t.write).addCallback(done)
+ else:
+ t = yield self._newStoreCalendarObject.createAttachmentWithName(
+ self.attachmentName,
+ content_type,
+ )
+ yield readStream(request.stream, t.write)
+ self._newStoreAttachment = self._newStoreObject = yield self._newStoreCalendarObject.attachmentWithName(
+ self.attachmentName
+ )
+ yield t.loseConnection()
+ returnValue(CREATED)
@requiresPermissions(davxml.Read())
def http_GET(self, request):
+
+ if not self.exists():
+ log.debug("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
stream = ProducerStream()
class StreamProtocol(Protocol):
def dataReceived(self, data):
@@ -1001,11 +957,14 @@
@requiresPermissions(fromParent=[davxml.Unbind()])
@inlineCallbacks
def http_DELETE(self, request):
+ if not self.exists():
+ log.debug("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
yield self._newStoreCalendarObject.removeAttachmentWithName(
self._newStoreAttachment.name()
)
- del self._newStoreCalendarObject
- self.__class__ = ProtoCalendarAttachment
+ self._newStoreAttachment = self._newStoreCalendarObject = None
returnValue(NO_CONTENT)
@@ -1031,58 +990,177 @@
def isCollection(self):
return False
+class _CommonObjectResource(_NewStoreFileMetaDataHelper, CalDAVResource, FancyEqMixin):
+
+ _componentFromStream = None
+
+ def __init__(self, storeObject, parentObject, name, *args, **kw):
+ """
+ Construct a L{_CommonObjectResource} from an L{CommonObjectResource}.
+
+ @param storeObject: The storage for the object.
+ @type storeObject: L{txdav.common.CommonObjectResource}
+ """
+ super(_CommonObjectResource, self).__init__(*args, **kw)
+ self._initializeWithObject(storeObject, parentObject)
+ self._name = name
+ self._metadata = {}
+
+
+ def _initializeWithObject(self, storeObject, parentObject):
+ self._newStoreParent = parentObject
+ self._newStoreObject = storeObject
+ self._dead_properties = _NewStorePropertiesWrapper(
+ self._newStoreObject.properties()
+ ) if self._newStoreObject else NonePropertyStore(self)
+
+
+ def isCollection(self):
+ return False
+
+
+ def quotaSize(self, request):
+ return succeed(self._newStoreObject.size())
+
+
+ def text(self):
+ return self._newStoreObject.text()
+
+ def component(self):
+ return self._newStoreObject.component()
+
+ @inlineCallbacks
+ def render(self, request):
+ if not self.exists():
+ log.debug("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
+ output = yield self.text()
+
+ response = Response(200, {}, output)
+ response.headers.setHeader("content-type", self.contentType())
+ returnValue(response)
+
+
+ @requiresPermissions(fromParent=[davxml.Unbind()])
+ def http_DELETE(self, request):
+ """
+ Override http_DELETE to validate 'depth' header.
+ """
+ if not self.exists():
+ log.err("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
+ return self.storeRemove(request, True, request.uri)
+
+
+ @inlineCallbacks
+ def storeStream(self, stream):
+
+ # FIXME: direct tests
+ component = self._componentFromStream(
+ (yield allDataFromStream(stream))
+ )
+ if self._newStoreObject:
+ yield self._newStoreObject.setComponent(component)
+ returnValue(NO_CONTENT)
+ else:
+ self._newStoreObject = (yield self._newStoreParent.createObjectResourceWithName(
+ self.name(), component, self._metadata
+ ))
+
+ # Re-initialize to get stuff setup again now we have no object
+ self._initializeWithObject(self._newStoreObject, self._newStoreParent)
+
+ returnValue(CREATED)
+
+
+ @inlineCallbacks
+ def storeRemove(self, request, implicitly, where):
+ """
+ Delete this object.
+
+ @param request: Unused by this implementation; present for signature
+ compatibility with L{CalendarCollectionResource.storeRemove}.
+
+ @type request: L{twext.web2.iweb.IRequest}
+
+ @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}
+ """
+
+ # Do delete
+
+ yield self._newStoreParent.removeObjectResourceWithName(
+ self._newStoreObject.name()
+ )
+
+ # Re-initialize to get stuff setup again now we have no object
+ self._initializeWithObject(None, self._newStoreParent)
+
+ returnValue(NO_CONTENT)
+
+
class _CalendarObjectMetaDataMixin(object):
+ """
+ Dynamically create the required meta-data for an object resource
+ """
def _get_accessMode(self):
- return self._newStoreObject.accessMode
+ return self._newStoreObject.accessMode if self._newStoreObject else self._metadata.get("accessMode", None)
def _set_accessMode(self, value):
- self._newStoreObject.accessMode = value
+ if self._newStoreObject:
+ self._newStoreObject.accessMode = value
+ else:
+ self._metadata["accessMode"] = value
accessMode = property(_get_accessMode, _set_accessMode)
def _get_isScheduleObject(self):
- return self._newStoreObject.isScheduleObject
+ return self._newStoreObject.isScheduleObject if self._newStoreObject else self._metadata.get("isScheduleObject", None)
def _set_isScheduleObject(self, value):
- self._newStoreObject.isScheduleObject = value
+ if self._newStoreObject:
+ self._newStoreObject.isScheduleObject = value
+ else:
+ self._metadata["isScheduleObject"] = value
isScheduleObject = property(_get_isScheduleObject, _set_isScheduleObject)
def _get_scheduleEtags(self):
- return self._newStoreObject.scheduleEtags
+ return self._newStoreObject.scheduleEtags if self._newStoreObject else self._metadata.get("scheduleEtags", None)
def _set_scheduleEtags(self, value):
- self._newStoreObject.scheduleEtags = value
+ if self._newStoreObject:
+ self._newStoreObject.scheduleEtags = value
+ else:
+ self._metadata["scheduleEtags"] = value
scheduleEtags = property(_get_scheduleEtags, _set_scheduleEtags)
def _get_hasPrivateComment(self):
- return self._newStoreObject.hasPrivateComment
+ return self._newStoreObject.hasPrivateComment if self._newStoreObject else self._metadata.get("hasPrivateComment", None)
def _set_hasPrivateComment(self, value):
- self._newStoreObject.hasPrivateComment = value
+ if self._newStoreObject:
+ self._newStoreObject.hasPrivateComment = value
+ else:
+ self._metadata["hasPrivateComment"] = value
hasPrivateComment = property(_get_hasPrivateComment, _set_hasPrivateComment)
-class CalendarObjectResource(_NewStoreFileMetaDataHelper, _CalendarObjectMetaDataMixin, CalDAVResource, FancyEqMixin):
+class CalendarObjectResource(_CalendarObjectMetaDataMixin, _CommonObjectResource):
"""
A resource wrapping a calendar object.
"""
compareAttributes = '_newStoreObject'.split()
- def __init__(self, calendarObject, *args, **kw):
- """
- Construct a L{CalendarObjectResource} from an L{ICalendarObject}.
+ _componentFromStream = VCalendar.fromString
- @param calendarObject: The storage for the calendar object.
- @type calendarObject: L{txdav.caldav.icalendarstore.ICalendarObject}
- """
- super(CalendarObjectResource, self).__init__(*args, **kw)
- self._initializeWithObject(calendarObject)
-
-
@inlineCallbacks
def inNewTransaction(self, request):
"""
@@ -1101,60 +1179,20 @@
homeUID = ownerHome.uid()
txn = ownerHome.transaction().store().newTransaction(
"new transaction for " + self._newStoreObject.name())
- newObject = ((yield (yield (yield txn.calendarHomeWithUID(homeUID))
+ newParent = (yield (yield txn.calendarHomeWithUID(homeUID))
.calendarWithName(calendarName))
- .calendarObjectWithName(objectName)))
+ newObject = (yield newParent.calendarObjectWithName(objectName))
request._newStoreTransaction = txn
request._resourcesByURL.clear()
request._urlsByResource.clear()
- self._initializeWithObject(newObject)
+ self._initializeWithObject(newObject, newParent)
returnValue(txn)
- def isCollection(self):
- return False
+ iCalendarText = _CommonObjectResource.text
+ iCalendar = _CommonObjectResource.component
- def exists(self):
- # FIXME: Tests
- return True
-
-
- def quotaSize(self, request):
- return succeed(self._newStoreObject.size())
-
-
- def iCalendarText(self):
- return self._newStoreObject.iCalendarText()
-
-
- def iCalendar(self):
- return self._newStoreObject.component()
-
-
- def text(self):
- return self.iCalendarText()
-
-
- @requiresPermissions(fromParent=[davxml.Unbind()])
- def http_DELETE(self, request):
- """
- Override http_DELETE to validate 'depth' header.
- """
- return self.storeRemove(request, True, request.uri)
-
-
- @inlineCallbacks
- def storeStream(self, stream):
-
- # FIXME: direct tests
- component = vcomponent.VComponent.fromString(
- (yield allDataFromStream(stream))
- )
- yield self._newStoreObject.setComponent(component)
- returnValue(NO_CONTENT)
-
-
def validIfScheduleMatch(self, request):
"""
Check to see if the given request's C{If-Schedule-Tag-Match} header
@@ -1234,20 +1272,8 @@
if lock:
yield lock.acquire()
- storeCalendar = self._newStoreObject._calendar
- # Do delete
+ yield super(CalendarObjectResource, self).storeRemove(request, implicitly, where)
- # FIXME: public attribute please. Should ICalendar maybe just have
- # a delete() method?
- yield storeCalendar.removeCalendarObjectWithName(
- self._newStoreObject.name()
- )
-
- # FIXME: clean this up with a 'transform' method
- self._newStoreParentCalendar = storeCalendar
- del self._newStoreObject
- self.__class__ = ProtoCalendarObjectResource
-
# Do scheduling
if not isinbox and implicitly:
yield scheduler.doImplicitScheduling()
@@ -1265,74 +1291,6 @@
returnValue(NO_CONTENT)
- def _initializeWithObject(self, calendarObject):
- self._newStoreObject = calendarObject
- self._dead_properties = _NewStorePropertiesWrapper(
- self._newStoreObject.properties()
- )
-
-
- @classmethod
- def transform(cls, self, calendarObject):
- self.__class__ = cls
- self._initializeWithObject(calendarObject)
-
-
-
-class ProtoCalendarObjectResource(_CalendarObjectMetaDataMixin, CalDAVResource, FancyEqMixin):
-
- compareAttributes = '_newStoreParentCalendar'.split()
-
- def __init__(self, parentCalendar, name, *a, **kw):
- """
- We need to create an "empty" resource object here because resource meta-data does get
- changed before the actual calendar data is written. So we need some kind of "container" for
- that to ensure those meta-data values actually get pushed to the store when the resource is
- created.
- """
- super(ProtoCalendarObjectResource, self).__init__(*a, **kw)
- self._newStoreParentCalendar = parentCalendar
- self._newStoreObject = self._newStoreParentCalendar.emptyObjectWithName(name)
- self._name = name
-
-
- @inlineCallbacks
- def storeStream(self, stream):
- # FIXME: direct tests
- component = vcomponent.VComponent.fromString(
- (yield allDataFromStream(stream))
- )
- yield self._newStoreParentCalendar.createCalendarObjectWithName(
- self.name(), component, objectResource=self._newStoreObject
- )
- CalendarObjectResource.transform(
- self,
- (yield self._newStoreParentCalendar.calendarObjectWithName(
- self.name()
- ))
- )
- returnValue(CREATED)
-
-
- def createSimilarFile(self, name):
- return None
-
-
- def isCollection(self):
- return False
-
- def exists(self):
- # FIXME: tests
- return False
-
-
- def name(self):
- return self._name
-
- def quotaSize(self, request):
- return succeed(0)
-
-
class AddressBookCollectionResource(_CommonHomeChildCollectionMixin, CalDAVResource):
"""
Wrapper around a L{txdav.carddav.iaddressbook.IAddressBook}.
@@ -1345,7 +1303,6 @@
"""
self._childClass = AddressBookObjectResource
- self._protoChildClass = ProtoAddressBookObjectResource
super(AddressBookCollectionResource, self).__init__(*args, **kw)
self._initializeWithHomeChild(addressbook, home)
self._name = addressbook.name() if addressbook else name
@@ -1379,165 +1336,18 @@
"""
pass
-class AddressBookObjectResource(_NewStoreFileMetaDataHelper, CalDAVResource, FancyEqMixin):
+class AddressBookObjectResource(_CommonObjectResource):
"""
A resource wrapping a addressbook object.
"""
compareAttributes = '_newStoreObject'.split()
- def __init__(self, Object, *args, **kw):
- """
- Construct a L{AddressBookObjectResource} from an L{IAddressBookObject}.
+ _componentFromStream = VCard.fromString
- @param Object: The storage for the addressbook object.
- @type Object: L{txdav.carddav.iaddressbookstore.IAddressBookObject}
- """
- super(AddressBookObjectResource, self).__init__(*args, **kw)
- self._initializeWithObject(Object)
+ vCardText = _CommonObjectResource.text
- def isCollection(self):
- return False
-
-
- def exists(self):
- # FIXME: Tests
- return True
-
-
- def quotaSize(self, request):
- return succeed(self._newStoreObject.size())
-
-
- def vCardText(self, ignored=None):
- assert ignored is None, "This is a addressbook object, not a addressbook"
- return self._newStoreObject.vCardText()
-
-
- def text(self):
- return self.vCardText()
-
-
- @inlineCallbacks
- def render(self, request):
- output = yield self.vCardText()
-
- response = Response(200, {}, output)
- response.headers.setHeader("content-type", self.contentType())
- returnValue(response)
-
-
- @requiresPermissions(fromParent=[davxml.Unbind()])
- def http_DELETE(self, request):
- """
- Override http_DELETE to validate 'depth' header.
- """
- return self.storeRemove(request, True, request.uri)
-
-
- @inlineCallbacks
- def storeStream(self, stream):
-
- # FIXME: direct tests
- component = VCard.fromString(
- (yield allDataFromStream(stream))
- )
- yield self._newStoreObject.setComponent(component)
- returnValue(NO_CONTENT)
-
-
- @inlineCallbacks
- def storeRemove(self, request, viaRequest, where):
- """
- Remove this addressbook object.
- """
-
- try:
-
- storeAddressBook = self._newStoreObject._addressbook
-
- # Do delete
-
- # FIXME: public attribute please
- yield storeAddressBook.removeAddressBookObjectWithName(
- self._newStoreObject.name()
- )
-
- # FIXME: clean this up with a 'transform' method
- self._newStoreParentAddressBook = storeAddressBook
- del self._newStoreObject
- self.__class__ = ProtoAddressBookObjectResource
-
- except MemcacheLockTimeoutError:
- raise HTTPError(StatusResponse(CONFLICT, "Resource: %s currently in use on the server." % (where,)))
-
- returnValue(NO_CONTENT)
-
-
- def _initializeWithObject(self, Object):
- self._newStoreObject = Object
- self._dead_properties = _NewStorePropertiesWrapper(
- self._newStoreObject.properties()
- )
-
-
- @classmethod
- def transform(cls, self, Object):
- self.__class__ = cls
- self._initializeWithObject(Object)
-
-
-
-class ProtoAddressBookObjectResource(CalDAVResource, FancyEqMixin):
-
- compareAttributes = '_newStoreParentAddressBook'.split()
-
- def __init__(self, parentAddressBook, name, *a, **kw):
- super(ProtoAddressBookObjectResource, self).__init__(*a, **kw)
- self._newStoreParentAddressBook = parentAddressBook
- self._name = name
-
-
- @inlineCallbacks
- def storeStream(self, stream):
- # FIXME: direct tests
- component = VCard.fromString(
- (yield allDataFromStream(stream))
- )
- yield self._newStoreParentAddressBook.createAddressBookObjectWithName(
- self.name(), component
- )
- AddressBookObjectResource.transform(
- self,
- (yield self._newStoreParentAddressBook.addressbookObjectWithName(
- self.name())
- )
- )
- returnValue(CREATED)
-
-
- def createSimilarFile(self, name):
- return None
-
-
- def isCollection(self):
- return False
-
-
- def exists(self):
- # FIXME: tests
- return False
-
-
- def name(self):
- return self._name
-
- def quotaSize(self, request):
- # FIXME: tests, workingness
- return succeed(0)
-
-
class _NotificationChildHelper(object):
"""
Methods for things which are like notification objects.
@@ -1599,13 +1409,7 @@
yield self._newStoreNotifications.notificationObjectWithName(name)
)
- if newStoreObject is not None:
- similar = StoreNotificationObjectFile(newStoreObject, self)
- else:
- # FIXME: creation in http_PUT should talk to a specific resource
- # type; this is the domain of StoreCalendarObjectResource.
- # similar = ProtoCalendarObjectFile(self._newStoreCalendar, path)
- similar = ProtoStoreNotificationObjectFile(self._newStoreNotifications, self)
+ similar = StoreNotificationObjectFile(newStoreObject, self)
# FIXME: tests should be failing without this line.
# Specifically, http_PUT won't be committing its transaction properly.
@@ -1748,7 +1552,7 @@
-class StoreNotificationObjectFile(NotificationResource):
+class StoreNotificationObjectFile(_NewStoreFileMetaDataHelper, NotificationResource):
"""
A resource wrapping a calendar object.
"""
@@ -1764,35 +1568,17 @@
self._initializeWithObject(notificationObject)
+ def _initializeWithObject(self, notificationObject):
+ self._newStoreObject = notificationObject
+ self._dead_properties = _NewStorePropertiesWrapper(
+ self._newStoreObject.properties()
+ ) if self._newStoreObject else NonePropertyStore(self)
+
+
def isCollection(self):
return False
- def exists(self):
- # FIXME: Tests
- return True
-
-
- def etag(self):
- return ETag(self._newStoreObject.md5())
-
- def contentType(self):
- return self._newStoreObject.contentType()
-
- def contentLength(self):
- return self._newStoreObject.size()
-
- def lastModified(self):
- return self._newStoreObject.modified()
-
- def creationDate(self):
- return self._newStoreObject.created()
-
-
- def newStoreProperties(self):
- return self._newStoreObject.properties()
-
-
def quotaSize(self, request):
return succeed(self._newStoreObject.size())
@@ -1805,6 +1591,10 @@
@requiresPermissions(davxml.Read())
@inlineCallbacks
def http_GET(self, request):
+ if not self.exists():
+ log.err("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
returnValue(
Response(OK, {"content-type":self.contentType()},
MemoryStream((yield self.text())))
@@ -1816,6 +1606,10 @@
"""
Override http_DELETE to validate 'depth' header.
"""
+ if not self.exists():
+ log.err("Resource not found: %s" % (self,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
return self.storeRemove(request, request.uri)
@@ -1835,50 +1629,9 @@
self._newStoreObject.name()
)
- # FIXME: clean this up with a 'transform' method
- self._newStoreParentNotifications = storeNotifications
- del self._newStoreObject
- self.__class__ = ProtoStoreNotificationObjectFile
+ self._initializeWithObject(None)
except MemcacheLockTimeoutError:
raise HTTPError(StatusResponse(CONFLICT, "Resource: %s currently in use on the server." % (where,)))
returnValue(NO_CONTENT)
-
-
- def _initializeWithObject(self, notificationObject):
- self._newStoreObject = notificationObject
- self._dead_properties = _NewStorePropertiesWrapper(
- self._newStoreObject.properties()
- )
-
-
- @classmethod
- def transform(cls, self, notificationObject):
- self.__class__ = cls
- self._initializeWithObject(notificationObject)
-
-
-
-class ProtoStoreNotificationObjectFile(NotificationResource):
-
- def __init__(self, parentNotifications, *a, **kw):
- super(ProtoStoreNotificationObjectFile, self).__init__(*a, **kw)
- self._newStoreParentNotifications = parentNotifications
-
-
- def isCollection(self):
- return False
-
-
- def exists(self):
- # FIXME: tests
- return False
-
-
- def quotaSize(self, request):
- # FIXME: tests, workingness
- return succeed(0)
-
-
-
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -229,9 +229,16 @@
"""
implements(ICalendarObject)
- def __init__(self, name, calendar):
+ def __init__(self, name, calendar, metadata=None):
super(CalendarObject, self).__init__(name, calendar)
self._attachments = {}
+
+ if metadata is None:
+ metadata = {}
+ self.accessMode = metadata.get("accessMode", "")
+ self.isScheduleObject = metadata.get("isScheduleObject", False)
+ self.scheduleEtags = metadata.get("scheduleEtags", "")
+ self.hasPrivateComment = metadata.get("hasPrivateComment", False)
@property
@@ -366,7 +373,11 @@
return str(self.properties().get(PropertyName.fromElement(TwistedCalendarAccessProperty), ""))
def _set_accessMode(self, value):
- self.properties()[PropertyName.fromElement(TwistedCalendarAccessProperty)] = TwistedCalendarAccessProperty(value)
+ pname = PropertyName.fromElement(TwistedCalendarAccessProperty)
+ if value:
+ self.properties()[pname] = TwistedCalendarAccessProperty(value)
+ elif pname in self.properties():
+ del self.properties()[pname]
accessMode = property(_get_accessMode, _set_accessMode)
@@ -374,7 +385,11 @@
return str(self.properties().get(PropertyName.fromElement(TwistedSchedulingObjectResource), "false")) == "true"
def _set_isScheduleObject(self, value):
- self.properties()[PropertyName.fromElement(TwistedSchedulingObjectResource)] = TwistedSchedulingObjectResource.fromString("true" if value else "false")
+ pname = PropertyName.fromElement(TwistedSchedulingObjectResource)
+ if value:
+ self.properties()[pname] = TwistedSchedulingObjectResource.fromString("true" if value else "false")
+ elif pname in self.properties():
+ del self.properties()[pname]
isScheduleObject = property(_get_isScheduleObject, _set_isScheduleObject)
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -239,15 +239,17 @@
class CalendarObject(CommonObjectResource):
implements(ICalendarObject)
- def __init__(self, calendar, name, uid):
+ def __init__(self, calendar, name, uid, metadata=None):
super(CalendarObject, self).__init__(calendar, name, uid)
self._objectTable = CALENDAR_OBJECT_TABLE
- self._access = accessMode_to_type[""]
- self._schedule_object = False
- self._schedule_etags = ""
- self._private_comments = False
+ if metadata is None:
+ metadata = {}
+ self.accessMode = metadata.get("accessMode", "")
+ self.isScheduleObject = metadata.get("isScheduleObject", False)
+ self.scheduleEtags = metadata.get("scheduleEtags", "")
+ self.hasPrivateComment = metadata.get("hasPrivateComment", False)
@inlineCallbacks
Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/file.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -151,7 +151,7 @@
"""
implements(IAddressBookObject)
- def __init__(self, name, addressbook):
+ def __init__(self, name, addressbook, metadata=None):
super(AddressBookObject, self).__init__(name, addressbook)
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -166,7 +166,7 @@
implements(IAddressBookObject)
- def __init__(self, addressbook, name, uid):
+ def __init__(self, addressbook, name, uid, metadata=None):
super(AddressBookObject, self).__init__(addressbook, name, uid)
self._objectTable = ADDRESSBOOK_OBJECT_TABLE
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -678,9 +678,9 @@
L{AddressBookObjectNameAlreadyExistsError} if a addressbook object with the
given name already exists in that addressbook.
"""
- self.failUnlessFailure(
- maybeDeferred((yield self.addressbookUnderTest())
- .createAddressBookObjectWithName,
+ yield self.failUnlessFailure(
+ maybeDeferred(
+ (yield self.addressbookUnderTest()).createAddressBookObjectWithName,
"1.vcf", VComponent.fromString(vcard4_text)),
ObjectResourceNameAlreadyExistsError
)
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -700,19 +700,11 @@
return obj
- def emptyObjectWithName(self, name):
- """
- Sometimes we need an "empty" object that we can store meta-data on prior to actually
- creating it with "real" data.
- """
- return self._objectResourceClass(name, self)
-
@writeOperation
- def createObjectResourceWithName(self, name, component, objectResource=None):
+ def createObjectResourceWithName(self, name, component, metadata=None):
"""
- As per L{emptyObjectWithName} we may get passed an already created object which we need
- to use for the one being created in order to preserve any meta-data that might have been
- set prior to the actual creation of the data.
+ Create a new resource with component data and optional metadata. We create the
+ python object using the metadata then create the actual store object with setComponent.
"""
if name.startswith("."):
raise ObjectResourceNameNotAllowedError(name)
@@ -721,15 +713,16 @@
if objectResourcePath.exists():
raise ObjectResourceNameAlreadyExistsError(name)
- if objectResource is None:
- objectResource = self._objectResourceClass(name, self)
+ objectResource = self._objectResourceClass(name, self, metadata)
objectResource.setComponent(component, inserting=True)
self._cachedObjectResources[name] = objectResource
# Note: setComponent triggers a notification, so we don't need to
# call notify( ) here like we do for object removal.
+ return objectResource
+
@writeOperation
def removeObjectResourceWithName(self, name):
if name.startswith("."):
@@ -831,7 +824,7 @@
compareAttributes = '_name _parentCollection'.split()
- def __init__(self, name, parent):
+ def __init__(self, name, parent, metadata=None):
self._name = name
self._parentCollection = parent
self._transaction = parent._transaction
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2010-11-08 17:26:37 UTC (rev 6578)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2010-11-09 16:36:32 UTC (rev 6579)
@@ -924,8 +924,7 @@
"""
We create the empty object first then have it initialize itself from the store
"""
- objectResource = self._objectResourceClass(self, name, uid)
- objectResource = (yield objectResource.initFromStore())
+ objectResource = (yield self._objectResourceClass.objectWithName(self, name, uid))
if objectResource:
self._objects[objectResource.name()] = objectResource
self._objects[objectResource.uid()] = objectResource
@@ -976,45 +975,24 @@
self._objects[name] = None
returnValue(None)
- def emptyObjectWithName(self, name):
- """
- Sometimes we need an "empty" object that we can store meta-data on prior to actually
- creating it with "real" data.
- """
- return self._objectResourceClass(self, name, None)
-
@inlineCallbacks
- def createObjectResourceWithName(self, name, component, objectResource=None):
+ def createObjectResourceWithName(self, name, component, metadata=None):
"""
- As per L{emptyObjectWithName} we may get passed an already created object which we need
- to use for the one being created in order to preserve any meta-data that might have been
- set prior to the actual creation of the data.
+ Create a new resource with component data and optional metadata. We create the
+ python object using the metadata then create the actual store object with setComponent.
"""
- if name.startswith("."):
- raise ObjectResourceNameNotAllowedError(name)
-
if name in self._objects:
if self._objects[name]:
raise ObjectResourceNameAlreadyExistsError()
- else:
- rows = yield self._txn.execSQL(
- "select %(column_RESOURCE_ID)s from %(name)s "
- "where %(column_RESOURCE_NAME)s = %%s "
- "and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
- [name, self._resourceID]
- )
- if rows:
- raise ObjectResourceNameAlreadyExistsError()
- if objectResource is None:
- objectResource = self._objectResourceClass(self, name, None)
- yield objectResource.setComponent(component, inserting=True)
+ objectResource = (yield self._objectResourceClass.create(self, name, component, metadata))
self._objects[objectResource.name()] = objectResource
self._objects[objectResource.uid()] = objectResource
- # Note: setComponent triggers a notification, so we don't need to
+ # Note: create triggers a notification when the component is set, so we don't need to
# call notify( ) here like we do for object removal.
+ returnValue(objectResource)
@inlineCallbacks
def removeObjectResourceWithName(self, name):
@@ -1314,7 +1292,7 @@
_objectTable = None
- def __init__(self, parent, name, uid):
+ def __init__(self, parent, name, uid, metadata=None):
self._parentCollection = parent
self._resourceID = None
self._name = name
@@ -1326,7 +1304,32 @@
self._objectText = None
+ @classmethod
+ def objectWithName(cls, parent, name, uid):
+ objectResource = cls(parent, name, uid)
+ return objectResource.initFromStore()
+
+ @classmethod
@inlineCallbacks
+ def create(cls, parent, name, component, metadata):
+
+ child = (yield cls.objectWithName(parent, name, None))
+ if child:
+ raise ObjectResourceNameAlreadyExistsError(name)
+
+ if name.startswith("."):
+ raise ObjectResourceNameNotAllowedError(name)
+
+ objectResource = cls(parent, name, None, metadata)
+ yield objectResource.setComponent(component, inserting=True)
+ yield objectResource._loadPropertyStore()
+
+ # Note: setComponent triggers a notification, so we don't need to
+ # call notify( ) here like we do for object removal.
+
+ returnValue(objectResource)
+
+ @inlineCallbacks
def initFromStore(self):
"""
Initialise this object from the store. We read in and cache all the extra metadata
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20101109/6a800973/attachment-0001.html>
More information about the calendarserver-changes
mailing list