[CalendarServer-changes] [5926] CalendarServer/branches/new-store-no-caldavfile
source_changes at macosforge.org
source_changes at macosforge.org
Fri Jul 23 10:06:53 PDT 2010
Revision: 5926
http://trac.macosforge.org/projects/calendarserver/changeset/5926
Author: cdaboo at apple.com
Date: 2010-07-23 10:06:51 -0700 (Fri, 23 Jul 2010)
Log Message:
-----------
Remove twistedcaldav.static.py! Currently passes all CDT tests. Unit tests still broken. Tools
broken. Much more clean-up, re-factoring required.
Modified Paths:
--------------
CalendarServer/branches/new-store-no-caldavfile/calendarserver/provision/root.py
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/acl.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/copymove.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete_common.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/get.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/mkcol.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/propfind.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/proppatch.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/put.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/report.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/resource.py
CalendarServer/branches/new-store-no-caldavfile/twext/web2/server.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/opendirectorybacker.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/index.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/__init__.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove_contact.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/get.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/mkcol.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_addressbook_common.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_addressbook_query.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_calendar_query.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_common.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_freebusy.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/caldav.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/processing.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharedcollection.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/simpleresource.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/vcardindex.py
CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py
CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py
CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py
Removed Paths:
-------------
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/provision/root.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/provision/root.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/provision/root.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -19,20 +19,20 @@
"RootResource",
]
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twext.python.log import Logger
from twext.web2 import responsecode
+from twext.web2.auth.wrapper import UnauthorizedResponse
from twext.web2.dav import davxml
from twext.web2.http import HTTPError, StatusResponse
-from twext.web2.auth.wrapper import UnauthorizedResponse
+
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.web.xmlrpc import Proxy
-from twext.python.log import Logger
-
-from twistedcaldav.resource import CalDAVComplianceMixIn
+from twistedcaldav.config import config
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
from twistedcaldav.extensions import DirectoryPrincipalPropertySearchMixIn
from twistedcaldav.extensions import ReadOnlyResourceMixIn
-from twistedcaldav.config import config
+from twistedcaldav.resource import CalDAVComplianceMixIn
log = Logger()
@@ -69,7 +69,6 @@
self.contentFilters.append((addConnectionClose, True))
def deadProperties(self):
- # FIXME: Same as in static.py's CalDAVFile
if not hasattr(self, "_dead_properties"):
# Get the property store from super
deadProperties = super(RootResource, self).deadProperties()
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -23,17 +23,18 @@
import os
from time import sleep
-from twisted.python.reflect import namedClass
-from twisted.internet.reactor import addSystemEventTrigger
-from twisted.cred.portal import Portal
-from twext.web2.http_headers import Headers
-from twext.web2.dav import auth
+from twext.python.filepath import CachingFilePath as FilePath
+from twext.python.log import Logger
from twext.web2.auth.basic import BasicCredentialFactory
+from twext.web2.dav import auth
+from twext.web2.http_headers import Headers
from twext.web2.resource import RedirectResource
from twext.web2.static import File as FileResource
-from twext.python.filepath import CachingFilePath as FilePath
-from twext.python.log import Logger
+from twisted.cred.portal import Portal
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.reactor import addSystemEventTrigger
+from twisted.python.reflect import namedClass
from twistedcaldav import memcachepool
from twistedcaldav.bind import doBind
@@ -47,15 +48,14 @@
from twistedcaldav.directory.sudo import SudoDirectoryService
from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.directory.wiki import WikiDirectoryService
+from twistedcaldav.directorybackedaddressbook import DirectoryBackedAddressBookResource
from twistedcaldav.notify import installNotificationClient
from twistedcaldav.resource import CalDAVResource, AuthenticationWrapper
from twistedcaldav.schedule import IScheduleInboxResource
from twistedcaldav.simpleresource import SimpleResource
-from twistedcaldav.static import DirectoryBackedAddressBookFile
from twistedcaldav.timezones import TimezoneCache
from twistedcaldav.timezoneservice import TimezoneServiceResource
from twistedcaldav.util import getMemorySize, getNCPU
-from twisted.internet.defer import inlineCallbacks, returnValue
try:
from twistedcaldav.authkerb import NegotiateCredentialFactory
@@ -98,7 +98,7 @@
webCalendarResourceClass = WebCalendarResource
webAdminResourceClass = WebAdminResource
addressBookResourceClass = DirectoryAddressBookHomeProvisioningResource
- directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookFile
+ directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookResource
#
# Setup the Directory
@@ -309,7 +309,6 @@
log.info("Setting up directory address book: %r" % (directoryBackedAddressBookResourceClass,))
directoryBackedAddressBookCollection = directoryBackedAddressBookResourceClass(
- directoryPath,
principalCollections=(principalCollection,)
)
addSystemEventTrigger("after", "startup", directoryBackedAddressBookCollection.provisionDirectory)
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/acl.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/acl.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/acl.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -45,8 +45,8 @@
"""
Respond to a ACL request. (RFC 3744, section 8.1)
"""
- if not self.fp.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ if not self.exists():
+ log.err("File not found: %s" % (self,))
yield responsecode.NOT_FOUND
return
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/copymove.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/copymove.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/copymove.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -152,7 +152,7 @@
#
# Let's play it safe for now and ignore broken clients.
#
- if self.fp.isdir() and depth != "infinity":
+ if self.isCollection() and depth != "infinity":
msg = "Client sent illegal depth header value for MOVE: %s" % (depth,)
log.err(msg)
raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
@@ -192,7 +192,7 @@
#
if not self.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ log.err("File not found: %s" % (self,))
raise HTTPError(StatusResponse(
responsecode.NOT_FOUND,
"Source resource %s not found." % (request.uri,)
@@ -250,7 +250,7 @@
if destination.exists() and not overwrite:
log.err("Attempt to %s onto existing file without overwrite flag enabled: %s"
- % (request.method, destination.fp.path))
+ % (request.method, destination))
raise HTTPError(StatusResponse(
responsecode.PRECONDITION_FAILED,
"Destination %s already exists." % (destination_uri,)
@@ -260,9 +260,9 @@
# Make sure destination's parent exists
#
- if not destination.fp.parent().isdir():
+ if not destination.parent().isCollection():
log.err("Attempt to %s to a resource with no parent: %s"
- % (request.method, destination.fp.path))
+ % (request.method, destination))
raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection."))
return destination, destination_uri, depth
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -45,8 +45,8 @@
"""
Respond to a DELETE request. (RFC 2518, section 8.6)
"""
- if not self.fp.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ if not self.exists():
+ log.err("File not found: %s" % (self,))
raise HTTPError(responsecode.NOT_FOUND)
depth = request.headers.getHeader("depth", "infinity")
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete_common.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete_common.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/delete_common.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -41,8 +41,8 @@
"""
Handle a resource delete with proper quota etc updates
"""
- if not resource.fp.exists():
- log.err("File not found: %s" % (resource.fp.path,))
+ if not resource.exists():
+ log.err("File not found: %s" % (resource,))
raise HTTPError(responsecode.NOT_FOUND)
# Do quota checks before we start deleting things
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/get.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/get.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/get.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -36,17 +36,17 @@
def http_OPTIONS(self, request):
d = authorize(self, request)
- d.addCallback(lambda _: super(twext.web2.dav.static.DAVFile, self).http_OPTIONS(request))
+ d.addCallback(lambda _: super(twext.web2.dav.resource.DAVResource, self).http_OPTIONS(request))
return d
def http_HEAD(self, request):
d = authorize(self, request)
- d.addCallback(lambda _: super(twext.web2.dav.static.DAVFile, self).http_HEAD(request))
+ d.addCallback(lambda _: super(twext.web2.dav.resource.DAVResource, self).http_HEAD(request))
return d
def http_GET(self, request):
d = authorize(self, request)
- d.addCallback(lambda _: super(twext.web2.dav.static.DAVFile, self).http_GET(request))
+ d.addCallback(lambda _: super(twext.web2.dav.resource.DAVResource, self).http_GET(request))
return d
def authorize(self, request):
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/mkcol.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/mkcol.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/mkcol.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -53,14 +53,14 @@
yield x
x.getResult()
- if self.fp.exists():
+ if self.exists():
log.err("Attempt to create collection where file exists: %s"
- % (self.fp.path,))
+ % (self,))
raise HTTPError(responsecode.NOT_ALLOWED)
if not parent.isCollection():
log.err("Attempt to create collection with non-collection parent: %s"
- % (self.fp.path,))
+ % (self,))
raise HTTPError(StatusResponse(
responsecode.CONFLICT,
"Parent resource is not a collection."
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/propfind.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/propfind.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/propfind.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -51,7 +51,7 @@
Respond to a PROPFIND request. (RFC 2518, section 8.1)
"""
if not self.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ log.err("File not found: %s" % (self,))
raise HTTPError(responsecode.NOT_FOUND)
#
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/proppatch.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/proppatch.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/proppatch.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -46,8 +46,8 @@
"""
Respond to a PROPPATCH request. (RFC 2518, section 8.2)
"""
- if not self.fp.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ if not self.exists():
+ log.err("File not found: %s" % (self.path,))
raise HTTPError(responsecode.NOT_FOUND)
x = waitForDeferred(self.authorize(request, (davxml.WriteProperties(),)))
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/put.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/put.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/put.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -88,7 +88,7 @@
"""
Respond to a PUT request. (RFC 2518, section 8.7)
"""
- log.msg("Writing request stream to %s" % (self.fp.path,))
+ log.msg("Writing request stream to %s" % (self,))
#
# Don't pass in the request URI, since PUT isn't specified to be able
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/report.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/report.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/method/report.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -64,8 +64,8 @@
"""
Respond to a REPORT request. (RFC 3253, section 3.6)
"""
- if not self.fp.exists():
- log.err("File not found: %s" % (self.fp.path,))
+ if not self.exists():
+ log.err("File not found: %s" % (self,))
raise HTTPError(responsecode.NOT_FOUND)
#
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/resource.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/dav/resource.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -1356,7 +1356,7 @@
This implementation returns a supported privilege set
containing only the DAV:all privilege.
"""
- return succeed(allPrivilegeSet)
+ return succeed(davPrivilegeSet)
def currentPrivileges(self, request):
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twext/web2/server.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twext/web2/server.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twext/web2/server.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -445,6 +445,13 @@
self._urlsByResource[resource] = url
return resource
+ def _forgetResource(self, resource, url):
+ """
+ Remember the URL of a visited resource.
+ """
+ del self._resourcesByURL[url]
+ del self._urlsByResource[resource]
+
def urlForResource(self, resource):
"""
Looks up the URL of the given resource if this resource was found while
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -26,6 +26,10 @@
##
def doBind():
+ import twext.web2.dav.method
+ from twext.web2.dav.resource import DAVResource
+ bindMethods(twext.web2.dav.method, DAVResource)
+
import twistedcaldav.method
from twistedcaldav.resource import CalDAVResource
bindMethods(twistedcaldav.method, CalDAVResource)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -33,6 +33,7 @@
from twext.web2.dav.resource import TwistedACLInheritable
from twext.web2.dav.util import joinURL
from twext.web2.http import HTTPError
+from twext.web2.http_headers import ETag, MimeType
from twisted.internet.defer import succeed
@@ -43,6 +44,8 @@
from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource,\
DAVResourceWithChildrenMixin
+from uuid import uuid4
+
log = Logger()
# Use __underbars__ convention to avoid conflicts with directory resource types.
@@ -60,13 +63,19 @@
AutoProvisioningResourceMixIn,
ReadOnlyResourceMixIn,
CalDAVComplianceMixIn,
+ DAVResourceWithChildrenMixin,
DAVResource,
- DAVResourceWithChildrenMixin,
):
def defaultAccessControlList(self):
return config.ProvisioningResourceACL
+ def etag(self):
+ return ETag(str(uuid4()))
+ def contentType(self):
+ return MimeType("httpd", "unix-directory")
+
+
class DirectoryAddressBookHomeProvisioningResource (DirectoryAddressBookProvisioningResource):
"""
Resource which provisions address book home collections as needed.
@@ -79,8 +88,7 @@
assert directory is not None
assert url.endswith("/"), "Collection URL must end in '/'"
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryAddressBookHomeProvisioningResource, self).__init__()
self.directory = IDirectoryService(directory)
self._url = url
@@ -136,7 +144,10 @@
def isCollection(self):
return True
+ def displayName(self):
+ return "addressbooks"
+
class DirectoryAddressBookHomeTypeProvisioningResource (DirectoryAddressBookProvisioningResource):
"""
Resource which provisions address book home collections of a specific
@@ -150,8 +161,7 @@
assert parent is not None
assert recordType is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryAddressBookHomeTypeProvisioningResource, self).__init__()
self.directory = parent.directory
self.recordType = recordType
@@ -188,7 +198,7 @@
raise HTTPError(responsecode.FORBIDDEN)
def makeChild(self, name):
- raise HTTPError(responsecode.NOT_FOUND)
+ return None
##
# DAV
@@ -197,6 +207,9 @@
def isCollection(self):
return True
+ def displayName(self):
+ return self.recordType
+
##
# ACL
##
@@ -216,8 +229,7 @@
"""
assert parent is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryAddressBookHomeUIDProvisioningResource, self).__init__()
self.directory = parent.directory
self.parent = parent
@@ -283,6 +295,9 @@
def isCollection(self):
return True
+ def displayName(self):
+ return uidsResourceName
+
##
# ACL
##
@@ -294,7 +309,7 @@
return self.parent.principalForRecord(record)
-class DirectoryAddressBookHomeResource (AutoProvisioningResourceMixIn, DAVResource, DAVResourceWithChildrenMixin):
+class DirectoryAddressBookHomeResource (AutoProvisioningResourceMixIn, DAVResource):
"""
Address book home collection resource.
"""
@@ -305,8 +320,7 @@
assert parent is not None
assert record is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryAddressBookHomeResource, self).__init__()
self.record = record
self.parent = parent
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -28,14 +28,15 @@
"DirectoryCalendarHomeResource",
]
-from twisted.internet.defer import succeed
+from twext.python.log import Logger
from twext.web2 import responsecode
from twext.web2.dav import davxml
+from twext.web2.dav.resource import TwistedACLInheritable
+from twext.web2.dav.util import joinURL
from twext.web2.http import HTTPError
-from twext.web2.dav.util import joinURL
-from twext.web2.dav.resource import TwistedACLInheritable
+from twext.web2.http_headers import ETag, MimeType
-from twext.python.log import Logger
+from twisted.internet.defer import succeed
from twistedcaldav import caldavxml
from twistedcaldav.config import config
@@ -47,6 +48,8 @@
from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn,\
DirectoryReverseProxyResource
+from uuid import uuid4
+
log = Logger()
# Use __underbars__ convention to avoid conflicts with directory resource types.
@@ -64,13 +67,18 @@
AutoProvisioningResourceMixIn,
ReadOnlyResourceMixIn,
CalDAVComplianceMixIn,
+ DAVResourceWithChildrenMixin,
DAVResource,
- DAVResourceWithChildrenMixin,
):
def defaultAccessControlList(self):
return config.ProvisioningResourceACL
+ def etag(self):
+ return ETag(str(uuid4()))
+ def contentType(self):
+ return MimeType("httpd", "unix-directory")
+
class DirectoryCalendarHomeProvisioningResource (DirectoryCalendarProvisioningResource):
"""
Resource which provisions calendar home collections as needed.
@@ -83,8 +91,7 @@
assert directory is not None
assert url.endswith("/"), "Collection URL must end in '/'"
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryCalendarHomeProvisioningResource, self).__init__()
self.directory = IDirectoryService(directory)
self._url = url
@@ -140,6 +147,8 @@
def isCollection(self):
return True
+ def displayName(self):
+ return "calendars"
class DirectoryCalendarHomeTypeProvisioningResource (DirectoryCalendarProvisioningResource):
"""
@@ -154,8 +163,7 @@
assert parent is not None
assert recordType is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryCalendarHomeTypeProvisioningResource, self).__init__()
self.directory = parent.directory
self.recordType = recordType
@@ -192,7 +200,7 @@
raise HTTPError(responsecode.FORBIDDEN)
def makeChild(self, name):
- raise HTTPError(responsecode.NOT_FOUND)
+ return None
##
# DAV
@@ -201,6 +209,9 @@
def isCollection(self):
return True
+ def displayName(self):
+ return self.recordType
+
##
# ACL
##
@@ -211,7 +222,6 @@
def principalForRecord(self, record):
return self._parent.principalForRecord(record)
-
class DirectoryCalendarHomeUIDProvisioningResource (DirectoryCalendarProvisioningResource):
def __init__(self, parent):
@@ -220,8 +230,7 @@
"""
assert parent is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryCalendarHomeUIDProvisioningResource, self).__init__()
self.directory = parent.directory
self.parent = parent
@@ -287,6 +296,9 @@
def isCollection(self):
return True
+ def displayName(self):
+ return uidsResourceName
+
##
# ACL
##
@@ -298,7 +310,7 @@
return self.parent.principalForRecord(record)
-class DirectoryCalendarHomeResource (AutoProvisioningResourceMixIn, DAVResource, DAVResourceWithChildrenMixin):
+class DirectoryCalendarHomeResource (AutoProvisioningResourceMixIn, DAVResource):
"""
Calendar home collection resource.
"""
@@ -309,8 +321,7 @@
assert parent is not None
assert record is not None
- DAVResource.__init__(self)
- DAVResourceWithChildrenMixin.__init__(self)
+ super(DirectoryCalendarHomeResource, self).__init__()
self.record = record
self.parent = parent
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/opendirectorybacker.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/opendirectorybacker.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/opendirectorybacker.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -61,7 +61,6 @@
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.method.put_addressbook_common import StoreAddressObjectResource
from twistedcaldav.query import addressbookqueryfilter
-from twistedcaldav.static import CalDAVFile
from twistedcaldav.vcard import Component, Property
from xmlrpclib import datetime
@@ -398,64 +397,65 @@
yield updateLock.release()
updateLock = None
- tmpDirLock = self._tmpDirAddressBookLock
- self.log_debug("blocking on lock of: \"%s\")" % self._tmpDirAddressBookLockPath)
- yield tmpDirLock.acquire()
-
- try:
- self.log_info("Filling directory address book")
- startTime = time.time()
- newAddressBook = CalDAVFile(makeTmpFilename())
- yield newAddressBook.createAddressBookCollection()
- for key, record in records.items():
- try:
- vcard = record.vCard()
- # make up a destination
-
- fileName = unquote(record.uriName())
- destination = CalDAVFile(join(newAddressBook.fp.path, fileName))
- destination_uri = record.hRef()
-
- self.log_debug("Adding \"%s\", uri=\"%s\"" % (fileName, destination_uri, ))
- self.log_debug("VCard text =\n%s" % (record.vCardText(), ))
-
- yield StoreAddressObjectResource( request = None,
- sourceadbk = False,
- destinationadbk = True,
- destination = destination,
- destination_uri = destination_uri,
- destinationparent = newAddressBook,
- vcard = vcard,
- indexdestination = False,
- ).run()
- except:
- self.log_info("Could not add record %s" % (record,))
- del records[key]
- newAddressBookCTag = customxml.GETCTag(str(hash(self.baseGUID + ":" + self.realmName + ":" + "".join(str(hash(records[key])) for key in records.keys()))))
-
- self.log_info("Indexing new directory address book")
- newAddressBook.index().recreate()
- elaspedTime = time.time()-startTime
- self.log_info("Timing: Fill address book: %.1f ms (%d vcards, %.2f vcards/sec)" % (elaspedTime*1000, len(records), len(records)/elaspedTime))
-
- updateLock = self.updateLock()
- self.log_debug("blocking on lock of: \"%s\")" % self._updateLockPath)
- yield updateLock.acquire()
-
- self.log_debug("Swapping in new directory address book")
-
- # move old address book out of the way
- if self.directoryBackedAddressBook.fp.exists():
- os.rename(self.directoryBackedAddressBook.fp.path, makeTmpFilename())
-
- #move new one into place
- os.rename(newAddressBook.fp.path, self.directoryBackedAddressBook.fp.path)
- self.directoryBackedAddressBook.fp.restat()
-
- self.directoryBackedAddressBook.writeDeadProperty(newAddressBookCTag)
- finally:
- self.log_debug("unlocking: \"%s\")" % self._tmpDirAddressBookLockPath)
- yield tmpDirLock.release()
+ #FIXME: implement store based cache
+# tmpDirLock = self._tmpDirAddressBookLock
+# self.log_debug("blocking on lock of: \"%s\")" % self._tmpDirAddressBookLockPath)
+# yield tmpDirLock.acquire()
+#
+# try:
+# self.log_info("Filling directory address book")
+# startTime = time.time()
+# newAddressBook = CalDAVFile(makeTmpFilename())
+# yield newAddressBook.createAddressBookCollection()
+# for key, record in records.items():
+# try:
+# vcard = record.vCard()
+# # make up a destination
+#
+# fileName = unquote(record.uriName())
+# destination = CalDAVFile(join(newAddressBook.fp.path, fileName))
+# destination_uri = record.hRef()
+#
+# self.log_debug("Adding \"%s\", uri=\"%s\"" % (fileName, destination_uri, ))
+# self.log_debug("VCard text =\n%s" % (record.vCardText(), ))
+#
+# yield StoreAddressObjectResource( request = None,
+# sourceadbk = False,
+# destinationadbk = True,
+# destination = destination,
+# destination_uri = destination_uri,
+# destinationparent = newAddressBook,
+# vcard = vcard,
+# indexdestination = False,
+# ).run()
+# except:
+# self.log_info("Could not add record %s" % (record,))
+# del records[key]
+# newAddressBookCTag = customxml.GETCTag(str(hash(self.baseGUID + ":" + self.realmName + ":" + "".join(str(hash(records[key])) for key in records.keys()))))
+#
+# self.log_info("Indexing new directory address book")
+# newAddressBook.index().recreate()
+# elaspedTime = time.time()-startTime
+# self.log_info("Timing: Fill address book: %.1f ms (%d vcards, %.2f vcards/sec)" % (elaspedTime*1000, len(records), len(records)/elaspedTime))
+#
+# updateLock = self.updateLock()
+# self.log_debug("blocking on lock of: \"%s\")" % self._updateLockPath)
+# yield updateLock.acquire()
+#
+# self.log_debug("Swapping in new directory address book")
+#
+# # move old address book out of the way
+# if self.directoryBackedAddressBook.fp.exists():
+# os.rename(self.directoryBackedAddressBook.fp.path, makeTmpFilename())
+#
+# #move new one into place
+# os.rename(newAddressBook.fp.path, self.directoryBackedAddressBook.fp.path)
+# self.directoryBackedAddressBook.fp.restat()
+#
+# self.directoryBackedAddressBook.writeDeadProperty(newAddressBookCTag)
+# finally:
+# self.log_debug("unlocking: \"%s\")" % self._tmpDirAddressBookLockPath)
+# yield tmpDirLock.release()
if not keepLock:
self.log_debug("unlocking: \"%s\")" % self._updateLockPath)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -22,22 +22,18 @@
"DirectoryBackedAddressBookResource",
]
-
-
from twext.python.log import Logger
-from twisted.internet.defer import succeed, inlineCallbacks, maybeDeferred, returnValue
-from twisted.python.reflect import namedClass
from twext.web2 import responsecode
from twext.web2.dav import davxml
from twext.web2.dav.resource import TwistedACLInheritable
from twext.web2.http import HTTPError, StatusResponse
+from twisted.internet.defer import succeed, inlineCallbacks, maybeDeferred, returnValue
+from twisted.python.reflect import namedClass
+
from twistedcaldav.config import config
from twistedcaldav.resource import CalDAVResource
-
-
-
log = Logger()
@@ -47,13 +43,36 @@
Directory-backed address book
"""
- def __init__(self):
+ def __init__(self, principalCollections):
- CalDAVResource.__init__(self)
+ CalDAVResource.__init__(self, principalCollections=principalCollections)
self.directory = None # creates directory attribute
+ # create with permissions, similar to CardDAVOptions in tap.py
+ # FIXME: /Directory does not need to be in file system unless debug-only caching options are used
+# try:
+# os.mkdir(path)
+# os.chmod(path, 0750)
+# if config.UserName and config.GroupName:
+# import pwd
+# import grp
+# uid = pwd.getpwnam(config.UserName)[2]
+# gid = grp.getgrnam(config.GroupName)[2]
+# os.chown(path, uid, gid)
+#
+# log.msg("Created %s" % (path,))
+#
+# except (OSError,), e:
+# # this is caused by multiprocessor race and is harmless
+# if e.errno != errno.EEXIST:
+# raise
+
+ def makeChild(self, name):
+ from twistedcaldav.simpleresource import SimpleCalDAVResource
+ return SimpleCalDAVResource(principalCollections=self.principalCollections())
+
def provisionDirectory(self):
if self.directory is None:
directoryClass = namedClass(config.DirectoryAddressBook.type)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -61,7 +61,7 @@
import twistedcaldav
from twistedcaldav import customxml
from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.util import Alternator, printTracebacks
+from twistedcaldav.util import Alternator
from twistedcaldav.directory.sudo import SudoDirectoryService
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.method.report import http_REPORT
@@ -718,8 +718,9 @@
Bits needed from twext.web2.static
"""
- def __init__(self):
+ def __init__(self, principalCollections=None):
self.putChildren = {}
+ super(DAVResourceWithChildrenMixin, self).__init__(principalCollections=principalCollections)
def putChild(self, name, child):
"""
@@ -742,7 +743,7 @@
result = self.makeChild(name)
return result
- def makeChild(self):
+ def makeChild(self, name):
# Subclasses with real children need to override this and return the appropriate object
return None
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/index.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/index.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/index.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -113,7 +113,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource to
+ @param resource: the L{CalDAVResource} resource to
index. C{resource} must be a calendar collection (ie.
C{resource.isPseudoCalendarCollection()} returns C{True}.)
"""
@@ -419,7 +419,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource to
+ @param resource: the L{CalDAVResource} resource to
index.
"""
super(CalendarIndex, self).__init__(resource)
@@ -920,7 +920,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource to
+ @param resource: the L{CalDAVResource} resource to
index. C{resource} must be a calendar collection (i.e.
C{resource.isPseudoCalendarCollection()} returns C{True}.)
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -58,15 +58,14 @@
from twistedcaldav import ical, caldavxml
from twistedcaldav import memcachepool
from twistedcaldav.config import config
-from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.ical import Property
from twistedcaldav.localization import translationTo
+from twistedcaldav.resource import CalDAVResource
from twistedcaldav.schedule import deliverSchedulePrivilegeSet
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
from twistedcaldav.scheduling.itip import iTIPRequestStatus
from twistedcaldav.scheduling.scheduler import IMIPScheduler
from twistedcaldav.sql import AbstractSQLDatabase
-from twistedcaldav.static import CalDAVFile
from twistedcaldav.util import AuthorizedHTTPGetter
from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
@@ -174,7 +173,7 @@
-class IMIPInboxResource(CalDAVFile):
+class IMIPInboxResource(CalDAVResource):
"""
IMIP-delivery Inbox resource.
@@ -187,7 +186,7 @@
"""
assert parent is not None
- CalDAVFile.__init__(self, NotFilePath(isfile=True), principalCollections=parent.principalCollections())
+ CalDAVResource.__init__(self, principalCollections=parent.principalCollections())
self.parent = parent
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/__init__.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/__init__.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/__init__.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -17,8 +17,8 @@
"""
CalDAV methods.
-Modules in this package are imported by twistedcaldav.static in order to
-bind methods to CalDAVFile.
+Modules in this package are imported by twistedcaldav.resource in order to
+bind methods to CalDAVResource.
"""
__all__ = [
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -30,7 +30,7 @@
from twistedcaldav.resource import isAddressBookCollectionResource,\
isPseudoCalendarCollectionResource,\
- CalendarHomeResource, AddressBookHomeResource
+ CalendarHomeResource, AddressBookHomeResource, CalDAVResource
log = Logger()
@@ -51,5 +51,5 @@
raise HTTPError(responsecode.NOT_ALLOWED)
# Do normal ACL behavior
- response = (yield super(CalDAVFile, self).http_ACL(request))
+ response = (yield super(CalDAVResource, self).http_ACL(request))
returnValue(response)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -39,14 +39,8 @@
)
from twistedcaldav.resource import isCalendarCollectionResource,\
- isPseudoCalendarCollectionResource
+ isPseudoCalendarCollectionResource, CalDAVResource
-CalDAVFile = None # Pacify PyFlakes; this *should* be fixed, but
- # it's not actually an undefined name, as the
- # bottom of twistedcaldav.static fixes it up
- # for us before any functions in this module
- # are invoked.
-
log = Logger()
@inlineCallbacks
@@ -67,7 +61,7 @@
# Check with CardDAV first (XXX might want to check EnableCardDAV switch?)
result = yield maybeCOPYContact(self, request)
if result is KEEP_GOING:
- result = yield super(CalDAVFile, self).http_COPY(request)
+ result = yield super(CalDAVResource, self).http_COPY(request)
returnValue(result)
#
@@ -140,7 +134,7 @@
returnValue(result)
# Do default WebDAV action
- result = (yield super(CalDAVFile, self).http_MOVE(request))
+ result = (yield super(CalDAVResource, self).http_MOVE(request))
if is_calendar_collection:
# Do some clean up
@@ -215,7 +209,7 @@
sourcecal: True if source is in a calendar collection, False otherwise
sourceparent: The parent resource for the source
destination_uri: The URI of the destination resource
- destination: CalDAVFile of destination if special proccesing required,
+ destination: CalDAVResource of destination if special processing required,
None otherwise
destinationcal: True if the destination is in a calendar collection,
False otherwise
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove_contact.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove_contact.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/copymove_contact.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -193,7 +193,7 @@
sourceadbk: True if source is in an addressbook collection, False otherwise
sourceparent: The parent resource for the source
destination_uri: The URI of the destination resource
- destination: CalDAVFile of destination if special processing required,
+ destination: CalDAVResource of destination if special processing required,
None otherwise
destinationadbk: True if the destination is in an addressbook collection,
False otherwise
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/get.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/get.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/get.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -34,7 +34,8 @@
from twistedcaldav.customxml import TwistedCalendarAccessProperty,\
calendarserver_namespace
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
+from twistedcaldav.resource import isPseudoCalendarCollectionResource,\
+ CalDAVResource
@inlineCallbacks
def http_GET(self, request):
@@ -97,5 +98,5 @@
returnValue(response)
# Do normal GET behavior
- response = (yield super(CalDAVFile, self).http_GET(request))
+ response = (yield super(CalDAVResource, self).http_GET(request))
returnValue(response)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/mkcol.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/mkcol.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/mkcol.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -35,9 +35,9 @@
from twistedcaldav import caldavxml, carddavxml, mkcolxml
from twistedcaldav.config import config
-from twistedcaldav.resource import isAddressBookCollectionResource
+from twistedcaldav.resource import isAddressBookCollectionResource,\
+ CalDAVResource
from twistedcaldav.resource import isPseudoCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile
log = Logger()
@@ -182,6 +182,6 @@
else:
# No request body so it is a standard MKCOL
- result = yield super(CalDAVFile, self).http_MKCOL(request)
+ result = yield super(CalDAVResource, self).http_MKCOL(request)
returnValue(result)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -31,8 +31,8 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.method.put_common import StoreCalendarObjectResource
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile
+from twistedcaldav.resource import isPseudoCalendarCollectionResource,\
+ CalDAVResource
log = Logger()
@@ -118,7 +118,7 @@
raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
else:
- result = (yield super(CalDAVFile, self).http_PUT(request))
+ result = (yield super(CalDAVResource, self).http_PUT(request))
if not hasattr(request, "extendedLogItems"):
request.extendedLogItems = {}
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_addressbook_common.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_addressbook_common.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_addressbook_common.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -97,16 +97,16 @@
Function that does common PUT/COPY/MOVE behavior.
@param request: the L{twext.web2.server.Request} for the current HTTP request.
- @param source: the L{CalDAVFile} for the source resource to copy from, or None if source data
+ @param source: the L{CalDAVResource} for the source resource to copy from, or None if source data
is to be read from the request.
@param source_uri: the URI for the source resource.
- @param destination: the L{CalDAVFile} for the destination resource to copy into.
+ @param destination: the L{CalDAVResource} for the destination resource to copy into.
@param destination_uri: the URI for the destination resource.
@param vcard: the C{str} or L{Component} vcard data if there is no source, None otherwise.
@param sourceadbk: True if the source resource is in a vcard collection, False otherwise.
@param destinationadbk: True if the destination resource is in a vcard collection, False otherwise
- @param sourceparent: the L{CalDAVFile} for the source resource's parent collection, or None if source is None.
- @param destinationparent: the L{CalDAVFile} for the destination resource's parent collection.
+ @param sourceparent: the L{CalDAVResource} for the source resource's parent collection, or None if source is None.
+ @param destinationparent: the L{CalDAVResource} for the destination resource's parent collection.
@param deletesource: True if the source resource is to be deleted on successful completion, False otherwise.
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -135,16 +135,16 @@
Function that does common PUT/COPY/MOVE behavior.
@param request: the L{twext.web2.server.Request} for the current HTTP request.
- @param source: the L{CalDAVFile} for the source resource to copy from, or None if source data
+ @param source: the L{CalDAVResource} for the source resource to copy from, or None if source data
is to be read from the request.
@param source_uri: the URI for the source resource.
- @param destination: the L{CalDAVFile} for the destination resource to copy into.
+ @param destination: the L{CalDAVResource} for the destination resource to copy into.
@param destination_uri: the URI for the destination resource.
@param calendar: the C{str} or L{Component} calendar data if there is no source, None otherwise.
@param sourcecal: True if the source resource is in a calendar collection, False otherwise.
@param destinationcal: True if the destination resource is in a calendar collection, False otherwise
- @param sourceparent: the L{CalDAVFile} for the source resource's parent collection, or None if source is None.
- @param destinationparent: the L{CalDAVFile} for the destination resource's parent collection.
+ @param sourceparent: the L{CalDAVResource} for the source resource's parent collection, or None if source is None.
+ @param destinationparent: the L{CalDAVResource} for the destination resource's parent collection.
@param deletesource: True if the source resource is to be deleted on successful completion, False otherwise.
@param isiTIP: True if relaxed calendar data validation is to be done, False otherwise.
@param allowImplicitSchedule: True if implicit scheduling should be attempted, False otherwise.
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_addressbook_query.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_addressbook_query.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_addressbook_query.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -104,7 +104,7 @@
"""
Run a query on the specified address book collection
accumulating the query responses.
- @param addrresource: the L{CalDAVFile} for an address book collection.
+ @param addrresource: the L{CalDAVResource} for an address book collection.
@param uri: the uri for the address book collecton resource.
"""
@@ -117,7 +117,7 @@
def queryAddressBookObjectResource(resource, uri, name, vcard, query_ok = False):
"""
Run a query on the specified vcard.
- @param resource: the L{CalDAVFile} for the vcard.
+ @param resource: the L{CalDAVResource} for the vcard.
@param uri: the uri of the resource.
@param name: the name of the resource.
@param vcard: the L{Component} vcard read from the resource.
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_calendar_query.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_calendar_query.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from twistedcaldav.config import config
"""
CalDAV calendar-query report
@@ -37,6 +36,7 @@
from twistedcaldav.caldavxml import caldav_namespace,\
NumberOfRecurrencesWithinLimits
+from twistedcaldav.config import config
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.index import IndexedSearchException
from twistedcaldav.instance import TooManyInstancesError
@@ -114,14 +114,14 @@
"""
Run a query on the specified calendar collection
accumulating the query responses.
- @param calresource: the L{CalDAVFile} for a calendar collection.
+ @param calresource: the L{CalDAVResource} for a calendar collection.
@param uri: the uri for the calendar collecton resource.
"""
def queryCalendarObjectResource(resource, uri, name, calendar, timezone, query_ok=False, isowner=True):
"""
Run a query on the specified calendar.
- @param resource: the L{CalDAVFile} for the calendar.
+ @param resource: the L{CalDAVResource} for the calendar.
@param uri: the uri of the resource.
@param name: the name of the resource.
@param calendar: the L{Component} calendar read from the resource.
@@ -183,7 +183,7 @@
index_query_ok = False
if not names:
- return
+ returnValue(True)
# Now determine which valid resources are readable and which are not
ok_resources = []
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_common.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_common.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -81,7 +81,7 @@
down from the root. Return a MultiStatus element of all responses.
@param request: the L{IRequest} for the current request.
- @param resource: the L{CalDAVFile} representing the root to start scanning
+ @param resource: the L{CalDAVResource} representing the root to start scanning
for calendar collections.
@param depth: the depth to do the scan.
@param apply: the function to apply to each calendar collection located
@@ -118,7 +118,7 @@
down from the root. Return a MultiStatus element of all responses.
@param request: the L{IRequest} for the current request.
- @param resource: the L{CalDAVFile} representing the root to start scanning
+ @param resource: the L{CalDAVResource} representing the root to start scanning
for address book collections.
@param depth: the depth to do the scan.
@param apply: the function to apply to each address book collection located
@@ -154,7 +154,7 @@
@param request: the L{IRequest} for the current request.
@param responses: the list of responses to append the result of this method to.
@param href: the L{HRef} element of the resource being targeted.
- @param resource: the L{CalDAVFile} for the targeted resource.
+ @param resource: the L{CalDAVResource} for the targeted resource.
@param calendar: the L{Component} for the calendar for the resource. This may be None
if the calendar has not already been read in, in which case the resource
will be used to get the calendar if needed.
@@ -196,7 +196,7 @@
@param request: the L{IRequest} for the current request.
@param prop: the L{PropertyContainer} element for the properties of interest.
- @param resource: the L{CalDAVFile} for the targeted resource.
+ @param resource: the L{CalDAVResource} for the targeted resource.
@param calendar: the L{Component} for the calendar for the resource. This may be None
if the calendar has not already been read in, in which case the resource
will be used to get the calendar if needed.
@@ -221,7 +221,7 @@
Return property names for all properties on the specified resource.
@param request: the L{IRequest} for the current request.
@param prop: the L{PropertyContainer} element for the properties of interest.
- @param resource: the L{CalDAVFile} for the targeted resource.
+ @param resource: the L{CalDAVResource} for the targeted resource.
@param calendar: the L{Component} for the calendar for the resource. This may be None
if the calendar has not already been read in, in which case the resource
will be used to get the calendar if needed.
@@ -246,7 +246,7 @@
Return the specified properties on the specified resource.
@param request: the L{IRequest} for the current request.
@param prop: the L{PropertyContainer} element for the properties of interest.
- @param resource: the L{CalDAVFile} for the targeted resource.
+ @param resource: the L{CalDAVResource} for the targeted resource.
@param calendar: the L{Component} for the calendar for the resource. This may be None
if the calendar has not already been read in, in which case the resource
will be used to get the calendar if needed.
@@ -310,7 +310,7 @@
Return the specified properties on the specified resource.
@param request: the L{IRequest} for the current request.
@param props: a list of property elements or qname tuples for the properties of interest.
- @param resource: the L{CalDAVFile} for the targeted resource.
+ @param resource: the L{CalDAVResource} for the targeted resource.
@param calendar: the L{Component} for the calendar for the resource. This may be None
if the calendar has not already been read in, in which case the resource
will be used to get the calendar if needed.
@@ -389,7 +389,7 @@
Run a free busy report on the specified calendar collection
accumulating the free busy info for later processing.
@param request: the L{IRequest} for the current request.
- @param calresource: the L{CalDAVFile} for a calendar collection.
+ @param calresource: the L{CalDAVResource} for a calendar collection.
@param fbinfo: the array of busy periods to update.
@param timerange: the L{TimeRange} for the query.
@param matchtotal: the running total for the number of matches.
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_freebusy.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_freebusy.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/report_freebusy.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -62,7 +62,7 @@
"""
Run a free busy report on the specified calendar collection
accumulating the free busy info for later processing.
- @param calresource: the L{CalDAVFile} for a calendar collection.
+ @param calresource: the L{CalDAVResource} for a calendar collection.
@param uri: the uri for the calendar collecton resource.
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -137,7 +137,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource for
+ @param resource: the L{CalDAVResource} resource for
the notifications collection.)
"""
self.resource = resource
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -14,14 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from twistedcaldav.index import SyncTokenValidException
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
-from uuid import uuid4
-import datetime
-from twisted.python.failure import Failure
-from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeResource
-from twistedcaldav.linkresource import LinkResource, LinkFollowerMixIn
-from twistedcaldav.directory.internal import InternalDirectoryRecord
"""
CalDAV-aware resources.
@@ -37,10 +29,13 @@
"isAddressBookCollectionResource",
]
+from urlparse import urlsplit
+from uuid import uuid4
+import datetime
import urllib
-from urlparse import urlsplit
import uuid
+
from zope.interface import implements
from twext.python.log import LoggingMixIn
@@ -50,7 +45,9 @@
from twisted.internet import reactor
from twisted.internet.defer import Deferred, succeed, maybeDeferred, fail
from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
+from twisted.python.failure import Failure
+
+from twext.web2 import responsecode, http, http_headers
from twext.web2.dav import davxml
from twext.web2.dav.auth import AuthenticationWrapper as SuperAuthenticationWrapper
from twext.web2.dav.davxml import dav_namespace
@@ -58,31 +55,40 @@
from twext.web2.dav.resource import AccessDeniedError, DAVPrincipalCollectionResource,\
davPrivilegeSet
from twext.web2.dav.resource import TwistedACLInheritable
-from twext.web2.dav.util import joinURL, parentForURL, unimplemented, normalizeURL
+from twext.web2.dav.util import joinURL, parentForURL, normalizeURL
from twext.web2.http import HTTPError, RedirectResponse, StatusResponse, Response
from twext.web2.http_headers import MimeType
from twext.web2.stream import MemoryStream
from twistedcaldav import caldavxml, customxml
from twistedcaldav import carddavxml
+from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.carddavxml import carddav_namespace
-from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
-from twistedcaldav.customxml import TwistedCalendarAccessProperty
+from twistedcaldav.customxml import TwistedCalendarAccessProperty,\
+ TwistedScheduleMatchETags
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
+from twistedcaldav.datafilters.privateevents import PrivateEventFilter
+from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeResource
from twistedcaldav.directory.calendar import DirectoryCalendarHomeResource
+from twistedcaldav.directory.internal import InternalDirectoryRecord
from twistedcaldav.extensions import DAVResource, DAVPrincipalResource,\
- PropertyNotFoundError
+ PropertyNotFoundError, DAVResourceWithChildrenMixin
from twistedcaldav.ical import Component
from twistedcaldav.ical import Component as iComponent
+from twistedcaldav.ical import Property as iProperty
from twistedcaldav.ical import allowedComponents
from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
+from twistedcaldav.index import SyncTokenValidException, Index
+from twistedcaldav.linkresource import LinkResource
+from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+from twistedcaldav.notify import getNodeCacher, NodeCreationException
from twistedcaldav.notify import getPubSubConfiguration, getPubSubPath,\
ClientNotifier, getPubSubXMPPURI, getPubSubHeartbeatURI
-from twistedcaldav.notify import getNodeCacher, NodeCreationException
from twistedcaldav.sharing import SharedCollectionMixin, SharedHomeMixin
from twistedcaldav.vcard import Component as vComponent
+from twistedcaldav.vcardindex import AddressBookIndex
from txdav.common.icommondatastore import InternalDataStoreError
@@ -180,7 +186,7 @@
calendarPrivilegeSet = _calendarPrivilegeSet()
-class CalDAVResource (CalDAVComplianceMixIn, SharedCollectionMixin, DAVResource, LoggingMixIn):
+class CalDAVResource (CalDAVComplianceMixIn, SharedCollectionMixin, DAVResourceWithChildrenMixin, DAVResource, LoggingMixIn):
"""
CalDAV resource.
@@ -889,8 +895,14 @@
else:
return super(DAVResource, self).displayName()
else:
- return super(DAVResource, self).displayName()
+ result = super(DAVResource, self).displayName()
+ if not result:
+ result = self.name()
+ return result
+ def name(self):
+ return None
+
def resourceID(self):
if not self.hasDeadProperty(davxml.ResourceID.qname()):
uuidval = uuid.uuid4()
@@ -923,7 +935,7 @@
if not self.isCollection(): return False
try:
- resourcetype = self.readDeadProperty((dav_namespace, "resourcetype"))
+ resourcetype = self.resourceType()
except HTTPError, e:
assert e.response.code == responsecode.NOT_FOUND, (
"Unexpected response code: %s" % (e.response.code,)
@@ -995,14 +1007,6 @@
return completionDeferred
- def createCalendar(self, request):
- """
- See L{ICalDAVResource.createCalendar}.
- This implementation raises L{NotImplementedError}; a subclass must
- override it.
- """
- unimplemented(self)
-
@inlineCallbacks
def deletedCalendar(self, request):
"""
@@ -1104,29 +1108,6 @@
returnValue(PerUserDataFilter(accessUID).filter(caldata))
- def iCalendarRolledup(self, request):
- """
- See L{ICalDAVResource.iCalendarRolledup}.
-
- This implementation raises L{NotImplementedError}; a subclass must
- override it.
- """
- unimplemented(self)
-
- def iCalendarText(self, name=None):
- """
- See L{ICalDAVResource.iCalendarText}.
-
- This implementation returns the string representation (according to
- L{str}) of the object returned by L{iCalendar} when given the same
- arguments.
-
- Note that L{iCalendar} by default calls this method, which creates
- an infinite loop. A subclass must override one of both of these
- methods.
- """
- return str(self.iCalendar(name))
-
def iCalendarAddressDoNormalization(self, ical):
"""
Normalize calendar user addresses in the supplied iCalendar object into their
@@ -1155,14 +1136,6 @@
return principal
return None
- def createAddressBook(self, request):
- """
- See L{ICalDAVResource.createAddressBook}.
- This implementation raises L{NotImplementedError}; a subclass must
- override it.
- """
- unimplemented(self)
-
def vCard(self, name=None):
"""
See L{ICalDAVResource.vCard}.
@@ -1186,37 +1159,6 @@
except ValueError:
return None
- def vCardRolledup(self, request):
- """
- See L{ICalDAVResource.vCardRolledup}.
-
- This implementation raises L{NotImplementedError}; a subclass must
- override it.
- """
- unimplemented(self)
-
- def vCardText(self, name=None):
- """
- See L{ICalDAVResource.vCardText}.
-
- This implementation returns the string representation (according to
- L{str}) of the object returned by L{vCard} when given the same
- arguments.
-
- Note that L{vCard} by default calls this method, which creates
- an infinite loop. A subclass must override one of both of these
- methods.
- """
- return str(self.vCard(name))
-
- def vCardXML(self, name=None):
- """
- See L{ICalDAVResource.vCardXML}.
- This implementation returns an XML element constructed from the object
- returned by L{vCard} when given the same arguments.
- """
- return carddavxml.AddressData.fromAddress(self.vCard(name))
-
def supportedReports(self):
result = super(CalDAVResource, self).supportedReports()
result.append(davxml.Report(caldavxml.CalendarQuery(),))
@@ -1373,7 +1315,7 @@
assert self.isCollection()
# Need to lock
- lock = MemcacheLock("ResourceLock", self.fp.path, timeout=60.0)
+ lock = MemcacheLock("ResourceLock", self.resourceID(), timeout=60.0)
try:
try:
yield lock.acquire()
@@ -1436,6 +1378,465 @@
return succeed(True)
+ #
+ # Stuff from CalDAVFile
+ #
+
+ def checkPreconditions(self, request):
+ """
+ We override the base class to handle the special implicit scheduling weak ETag behavior
+ for compatibility with old clients using If-Match.
+ """
+
+ if config.Scheduling.CalDAV.ScheduleTagCompatibility:
+
+ if self.exists() and self.hasDeadProperty(TwistedScheduleMatchETags):
+ etags = self.readDeadProperty(TwistedScheduleMatchETags).children
+ if len(etags) > 1:
+ # This is almost verbatim from twext.web2.static.checkPreconditions
+ if request.method not in ("GET", "HEAD"):
+
+ # Loop over each tag and succeed if any one matches, else re-raise last exception
+ exists = self.exists()
+ last_modified = self.lastModified()
+ last_exception = None
+ for etag in etags:
+ try:
+ http.checkPreconditions(
+ request,
+ entityExists = exists,
+ etag = http_headers.ETag(etag),
+ lastModified = last_modified,
+ )
+ except HTTPError, e:
+ last_exception = e
+ else:
+ break
+ else:
+ if last_exception:
+ raise last_exception
+
+ # Check per-method preconditions
+ method = getattr(self, "preconditions_" + request.method, None)
+ if method:
+ response = maybeDeferred(method, request)
+ response.addCallback(lambda _: request)
+ return response
+ else:
+ return None
+
+ return super(CalDAVResource, self).checkPreconditions(request)
+
+ def createCalendar(self, request):
+ """
+ External API for creating a calendar. Verify that the parent is a
+ collection, exists, is I{not} a calendar collection; that this resource
+ does not yet exist, then create it.
+
+ @param request: the request used to look up parent resources to
+ validate.
+
+ @type request: L{twext.web2.iweb.IRequest}
+
+ @return: a deferred that fires when a calendar collection has been
+ created in this resource.
+ """
+ if self.exists():
+ self.log_error("Attempt to create collection where file exists: %s" % (self,))
+ raise HTTPError(StatusResponse(responsecode.NOT_ALLOWED, "File exists"))
+
+ # newStore guarantees that we always have a parent calendar home
+ #if not self.fp.parent().isdir():
+ # log.err("Attempt to create collection with no parent: %s" % (self.fp.path,))
+ # raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection"))
+
+ #
+ # Verify that no parent collection is a calendar also
+ #
+
+ def _defer(parent):
+ if parent is not None:
+ self.log_error("Cannot create a calendar collection within a calendar collection %s" % (parent,))
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (caldavxml.caldav_namespace, "calendar-collection-location-ok")
+ ))
+
+ return self.createCalendarCollection()
+
+ parent = self._checkParents(request, isPseudoCalendarCollectionResource)
+ parent.addCallback(_defer)
+ return parent
+
+
+ def createCalendarCollection(self):
+ """
+ Internal API for creating a calendar collection.
+
+ @return: a L{Deferred} which fires when the underlying collection has
+ actually been created.
+ """
+ return fail(NotImplementedError())
+
+
+ def createSpecialCollection(self, resourceType=None):
+ #
+ # Create the collection once we know it is safe to do so
+ #
+ def onCollection(status):
+ if status != responsecode.CREATED:
+ raise HTTPError(status)
+
+ self.writeDeadProperty(resourceType)
+ return status
+
+ def onError(f):
+ try:
+ rmdir(self.fp)
+ except Exception, e:
+ log.err("Unable to clean up after failed MKCOL (special resource type: %s): %s" % (e, resourceType,))
+ return f
+
+ d = mkcollection(self.fp)
+ if resourceType is not None:
+ d.addCallback(onCollection)
+ d.addErrback(onError)
+ return d
+
+ @inlineCallbacks
+ def iCalendarRolledup(self, request):
+ if self.isPseudoCalendarCollection():
+
+
+# FIXME: move cache implementation!
+ # Determine the cache key
+# isvirt = self.isVirtualShare()
+# if isvirt:
+# principal = (yield self.resourceOwnerPrincipal(request))
+# if principal:
+# cacheKey = principal.principalUID()
+# else:
+# cacheKey = "unknown"
+# else:
+# isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
+# cacheKey = "owner" if isowner else "notowner"
+
+ # Now check for a cached .ics
+# rolled = self.fp.child(".subscriptions")
+# if not rolled.exists():
+# try:
+# rolled.makedirs()
+# except IOError, e:
+# self.log_error("Unable to create internet calendar subscription cache directory: %s because of: %s" % (rolled.path, e,))
+# raise HTTPError(ErrorResponse(responsecode.INTERNAL_SERVER_ERROR))
+# cached = rolled.child(cacheKey)
+# if cached.exists():
+# try:
+# cachedData = cached.open().read()
+# except IOError, e:
+# self.log_error("Unable to open or read internet calendar subscription cache file: %s because of: %s" % (cached.path, e,))
+# else:
+# # Check the cache token
+# token, data = cachedData.split("\r\n", 1)
+# if token == self.getSyncToken():
+# returnValue(data)
+
+ # Generate a monolithic calendar
+ calendar = iComponent("VCALENDAR")
+ calendar.addProperty(iProperty("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 the checkPrivileges on each child.
+ filteredaces = (yield self.inheritedACEsforChildren(request))
+
+ tzids = set()
+ isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
+ accessPrincipal = (yield self.resourceOwnerPrincipal(request))
+
+ for name, uid, type in self.index().bruteForceSearch(): #@UnusedVariable
+ try:
+ child = yield request.locateChildResource(self, name)
+ except TypeError:
+ child = None
+
+ if child is not None:
+ # Check privileges of child - skip if access denied
+ try:
+ yield child.checkPrivileges(request, (davxml.Read(),), inherited_aces=filteredaces)
+ except AccessDeniedError:
+ continue
+
+ # Get the access filtered view of the data
+ caldata = child.iCalendarTextFiltered(isowner, accessPrincipal.principalUID() if accessPrincipal else "")
+ try:
+ subcalendar = iComponent.fromString(caldata)
+ except ValueError:
+ continue
+ assert subcalendar.name() == "VCALENDAR"
+
+ for component in subcalendar.subcomponents():
+
+ # Only insert VTIMEZONEs once
+ if component.name() == "VTIMEZONE":
+ tzid = component.propertyValue("TZID")
+ if tzid in tzids:
+ continue
+ tzids.add(tzid)
+
+ calendar.addComponent(component)
+
+ # Cache the data
+ data = str(calendar)
+ data = self.getSyncToken() + "\r\n" + data
+# try:
+# cached.open(mode='w').write(data)
+# except IOError, e:
+# self.log_error("Unable to open or write internet calendar subscription cache file: %s because of: %s" % (cached.path, e,))
+
+ returnValue(calendar)
+
+ raise HTTPError(ErrorResponse(responsecode.BAD_REQUEST))
+
+ def iCalendarTextFiltered(self, isowner, accessUID=None):
+ try:
+ access = self.readDeadProperty(TwistedCalendarAccessProperty)
+ except HTTPError:
+ access = None
+
+ # Now "filter" the resource calendar data
+ caldata = PrivateEventFilter(access, isowner).filter(self.iCalendarText())
+ if accessUID:
+ caldata = PerUserDataFilter(accessUID).filter(caldata)
+ return str(caldata)
+
+ def iCalendarText(self, name=None):
+ if self.isPseudoCalendarCollection():
+ if name is None:
+ return str(self.iCalendar())
+
+ calendar_resource = self.getChild(name)
+ return calendar_resource.iCalendarText()
+
+ elif self.isCollection():
+ return None
+
+ else:
+ if name is not None:
+ raise AssertionError("name must be None for non-collection calendar resource")
+
+ # FIXME: StoreBridge handles this case
+ raise NotImplementedError
+
+ def createAddressBook(self, request):
+ """
+ External API for creating an addressbook. Verify that the parent is a
+ collection, exists, is I{not} an addressbook collection; that this resource
+ does not yet exist, then create it.
+
+ @param request: the request used to look up parent resources to
+ validate.
+
+ @type request: L{twext.web2.iweb.IRequest}
+
+ @return: a deferred that fires when an addressbook collection has been
+ created in this resource.
+ """
+ #
+ # request object is required because we need to validate against parent
+ # resources, and we need the request in order to locate the parents.
+ #
+
+ if self.exists():
+ self.log_error("Attempt to create collection where file exists: %s" % (self,))
+ raise HTTPError(StatusResponse(responsecode.NOT_ALLOWED, "File exists"))
+
+ # newStore guarantees that we always have a parent calendar home
+ #if not os.path.isdir(os.path.dirname(self.fp.path)):
+ # log.err("Attempt to create collection with no parent: %s" % (self.fp.path,))
+ # raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection"))
+
+ #
+ # Verify that no parent collection is a calendar also
+ #
+
+ def _defer(parent):
+ if parent is not None:
+ self.log_error("Cannot create an address book collection within an address book collection %s" % (parent,))
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (carddavxml.carddav_namespace, "addressbook-collection-location-ok")
+ ))
+
+ return self.createAddressBookCollection()
+
+ parent = self._checkParents(request, isAddressBookCollectionResource)
+ parent.addCallback(_defer)
+ return parent
+
+ def createAddressBookCollection(self):
+ """
+ Internal API for creating an addressbook collection.
+
+ @return: a L{Deferred} which fires when the underlying collection has
+ actually been created.
+ """
+ return fail(NotImplementedError())
+
+ @inlineCallbacks
+ def vCardRolledup(self, request):
+ # TODO: just catenate all the vCards together
+ yield fail(HTTPError((ErrorResponse(responsecode.BAD_REQUEST))))
+
+ def vCardText(self, name=None):
+ if self.isAddressBookCollection():
+ if name is None:
+ return str(self.vCard())
+
+ vcard_resource = self.getChild(name)
+ return vcard_resource.vCardText()
+
+ elif self.isCollection():
+ return None
+
+ else:
+ if name is not None:
+ raise AssertionError("name must be None for non-collection vcard resource")
+
+ # FIXME: StoreBridge handles this case
+ raise NotImplementedError
+
+ def vCardXML(self, name=None):
+ return carddavxml.AddressData.fromAddressData(self.vCardText(name))
+
+ def supportedPrivileges(self, request):
+ # read-free-busy support on calendar collection and calendar object resources
+ if self.isCollection():
+ return succeed(calendarPrivilegeSet)
+ else:
+ def gotParent(parent):
+ if parent and isCalendarCollectionResource(parent):
+ return succeed(calendarPrivilegeSet)
+ else:
+ return super(CalDAVResource, self).supportedPrivileges(request)
+
+ d = self.locateParent(request, request.urlForResource(self))
+ d.addCallback(gotParent)
+ return d
+
+ return super(CalDAVResource, self).supportedPrivileges(request)
+
+ def index(self):
+ """
+ Obtains the index for a calendar collection resource.
+ @return: the index object for this resource.
+ @raise AssertionError: if this resource is not a calendar collection
+ resource.
+ """
+ if self.isAddressBookCollection():
+ return AddressBookIndex(self)
+ else:
+ return Index(self)
+
+ ##
+ # Quota
+ ##
+
+ def quotaSize(self, request):
+ """
+ Get the size of this resource.
+ TODO: Take into account size of dead-properties. Does stat include xattrs size?
+
+ @return: an L{Deferred} with a C{int} result containing the size of the resource.
+ """
+# if self.isCollection():
+# @inlineCallbacks
+# def walktree(top):
+# """
+# Recursively descend the directory tree rooted at top,
+# calling the callback function for each regular file
+#
+# @param top: L{FilePath} for the directory to walk.
+# """
+#
+# total = 0
+# for f in top.listdir():
+#
+# # Ignore the database
+# if f.startswith("."):
+# continue
+#
+# child = top.child(f)
+# if child.isdir():
+# # It's a directory, recurse into it
+# total += yield walktree(child)
+# elif child.isfile():
+# # It's a file, call the callback function
+# total += child.getsize()
+# else:
+# # Unknown file type, print a message
+# pass
+#
+# returnValue(total)
+#
+# return walktree(self.fp)
+# else:
+# return succeed(self.fp.getsize())
+ return succeed(0)
+
+ ##
+ # Utilities
+ ##
+
+ @staticmethod
+ def _isChildURI(request, uri, immediateChild=True):
+ """
+ Verify that the supplied URI represents a resource that is a child
+ of the request resource.
+ @param request: the request currently in progress
+ @param uri: the URI to test
+ @return: True if the supplied URI is a child resource
+ False if not
+ """
+ if uri is None: return False
+
+ #
+ # Parse the URI
+ #
+
+ (scheme, host, path, query, fragment) = urlsplit(uri) #@UnusedVariable
+
+ # Request hostname and child uri hostname have to be the same.
+ if host and host != request.headers.getHeader("host"):
+ return False
+
+ # Child URI must start with request uri text.
+ parent = request.uri
+ if not parent.endswith("/"):
+ parent += "/"
+
+ return path.startswith(parent) and (len(path) > len(parent)) and (not immediateChild or (path.find("/", len(parent)) == -1))
+
+ @inlineCallbacks
+ def _checkParents(self, request, test):
+ """
+ @param request: the request being processed.
+ @param test: a callable
+ @return: the closest parent for this resource using the request URI from
+ the given request for which C{test(parent)} evaluates to a true
+ value, or C{None} if no parent matches.
+ """
+ parent = self
+ parent_uri = request.uri
+
+ while True:
+ parent_uri = parentForURL(parent_uri)
+ if not parent_uri: break
+
+ parent = yield request.locateResource(parent_uri)
+
+ if test(parent):
+ returnValue(parent)
+
class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
"""
CalDAV principal collection.
@@ -1738,6 +2139,7 @@
self._newStoreCalendarHome = storeHome
CalDAVResource.__init__(self)
DirectoryCalendarHomeResource.__init__(self, parent, record)
+
from twistedcaldav.storebridge import _NewStorePropertiesWrapper
self._dead_properties = _NewStorePropertiesWrapper(
self._newStoreCalendarHome.properties()
@@ -1843,6 +2245,7 @@
ProtoCalendarCollectionResource)
similar = ProtoCalendarCollectionResource(
self._newStoreCalendarHome,
+ name,
principalCollections=self.principalCollections()
)
else:
@@ -1982,7 +2385,7 @@
http_ACL = None # ACL method not supported
-class AddressBookHomeResource (LinkFollowerMixIn, SharedHomeMixin, DirectoryAddressBookHomeResource, CalDAVResource):
+class AddressBookHomeResource (SharedHomeMixin, DirectoryAddressBookHomeResource, CalDAVResource):
"""
Address book home collection resource.
"""
@@ -2057,7 +2460,7 @@
if name == "notification" and config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled and not config.Sharing.Calendars.Enabled:
return self.createNotificationsCollection()
- return self.getAddressBookCollection(name)
+ return self.makeChild(name)
def createNotificationsCollection(self):
@@ -2075,7 +2478,7 @@
label="collection")
return similar
- def getAddressBookCollection(self, name):
+ def makeChild(self, name):
# Check for public/global path
from twistedcaldav.storebridge import (
@@ -2096,6 +2499,7 @@
# Local imports.due to circular dependency between modules.
similar = protoCls(
self._newStoreAddressBookHome,
+ name,
principalCollections=self.principalCollections()
)
else:
@@ -2113,10 +2517,7 @@
if name not in self.putChildren and name.lower() in (x.lower() for x in self.putChildren):
return None
- result = super(AddressBookHomeResource, self).getChild(name)
- if not result:
- result = self.getAddressBookCollection(name)
- return result
+ return super(AddressBookHomeResource, self).getChild(name)
def listChildren(self):
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -25,13 +25,12 @@
"IScheduleInboxResource",
]
-from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twext.web2 import responsecode
from twext.web2.dav import davxml
from twext.web2.dav.element.extensions import SyncCollection
from twext.web2.dav.element.rfc2518 import HRef
+from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
from twext.web2.dav.noneprops import NonePropertyStore
from twext.web2.dav.resource import davPrivilegeSet
from twext.web2.dav.util import joinURL, normalizeURL
@@ -39,12 +38,14 @@
from twext.web2.http import Response
from twext.web2.http_headers import MimeType
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+
from twistedcaldav import caldavxml
from twistedcaldav.caldavxml import caldav_namespace, Opaque,\
CalendarFreeBusySet, ScheduleCalendarTransp
from twistedcaldav.config import config
from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.extensions import DAVResource, DAVResourceWithChildrenMixin
+from twistedcaldav.extensions import DAVResource
from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn
from twistedcaldav.resource import isCalendarCollectionResource
from twistedcaldav.scheduling.scheduler import CalDAVScheduler, IScheduleScheduler
@@ -88,7 +89,7 @@
deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
-class CalendarSchedulingCollectionResource (CalDAVResource, DAVResourceWithChildrenMixin):
+class CalendarSchedulingCollectionResource (CalDAVResource):
"""
CalDAV principal resource.
@@ -101,8 +102,7 @@
"""
assert parent is not None
- CalDAVResource.__init__(self, principalCollections=parent.principalCollections())
- DAVResourceWithChildrenMixin.__init__(self)
+ super(CalendarSchedulingCollectionResource, self).__init__(principalCollections=parent.principalCollections())
self.parent = parent
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/caldav.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/caldav.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -133,7 +133,7 @@
@inlineCallbacks
def generateResponse(self, recipient, responses):
# Hash the iCalendar data for use as the last path element of the URI path
- name = md5(self.scheduler.calendardata + str(time.time()) + recipient.inbox.fp.path).hexdigest() + ".ics"
+ name = md5(self.scheduler.calendardata + str(time.time()) + recipient.inboxURL).hexdigest() + ".ics"
# Get a resource for the new item
childURL = joinURL(recipient.inboxURL, name)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/processing.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/processing.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -288,7 +288,7 @@
log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - new processed" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
autoprocessed = self.recipient.principal.getAutoSchedule()
new_calendar = iTipProcessing.processNewRequest(self.message, self.recipient.cuaddr, autoprocessing=autoprocessed)
- name = md5(str(new_calendar) + str(time.time()) + default.fp.path).hexdigest() + ".ics"
+ name = md5(str(new_calendar) + str(time.time()) + defaultURL).hexdigest() + ".ics"
# Handle auto-reply behavior
if autoprocessed:
@@ -616,15 +616,15 @@
resource or by creating a new one.
@param collURL: the C{str} containing the URL of the calendar collection.
- @param collection: the L{CalDAVFile} for the calendar collection to store the resource in.
+ @param collection: the L{CalDAVResource} for the calendar collection to store the resource in.
@param name: the C{str} for the resource name to write into, or {None} to write a new resource.
@param calendar: the L{Component} calendar to write.
- @return: L{Deferred} -> L{CalDAVFile}
+ @return: L{Deferred} -> L{CalDAVResource}
"""
# Create a new name if one was not provided
if name is None:
- name = md5(str(calendar) + str(time.time()) + collection.fp.path).hexdigest() + ".ics"
+ name = md5(str(calendar) + str(time.time()) + collURL).hexdigest() + ".ics"
# Get a resource for the new item
newchildURL = joinURL(collURL, name)
@@ -655,7 +655,7 @@
@param collURL: the URL of the calendar collection.
@type name: C{str}
@param collection: the calendar collection to delete the resource from.
- @type collection: L{CalDAVFile}
+ @type collection: L{CalDAVResource}
@param name: the resource name to write into, or {None} to write a new resource.
@type name: C{str}
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharedcollection.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharedcollection.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharedcollection.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -14,13 +14,14 @@
# limitations under the License.
##
+__all__ = [
+ "SharedCollectionResource",
+]
+
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.linkresource import LinkResource
-__all__ = [
- "SharedCollectionResource",
-]
"""
Sharing behavior
@@ -40,6 +41,12 @@
if not hasattr(self, "_linkedResource"):
self._linkedResource = (yield request.locateResource(self.share.hosturl))
+
+ # FIXME: this is awkward - because we are "mutation" this object into a virtual share
+ # we must not cache the resource at this URL, otherwise an access of the owner's resource
+ # will return the same virtually shared one which would be wrong.
+ request._forgetResource(self._linkedResource, self.share.hosturl)
+
ownerPrincipal = (yield self.parent.ownerPrincipal(request))
self._linkedResource.setVirtualShare(ownerPrincipal, self.share)
returnValue(self._linkedResource)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -24,15 +24,20 @@
from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
from twext.web2.dav.resource import TwistedACLInheritable
from twext.web2.dav.util import allDataFromStream, joinURL
-from twext.web2.http import HTTPError, Response, StatusResponse, XMLResponse
+from twext.web2.http import HTTPError, Response, XMLResponse
+
from twisted.internet.defer import succeed, inlineCallbacks, DeferredList,\
returnValue
+
from twistedcaldav import customxml, caldavxml
from twistedcaldav.config import config
from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.linkresource import LinkFollowerMixIn
from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
+
+from vobject.icalendar import dateTimeToString, utc
+
from uuid import uuid4
-from vobject.icalendar import dateTimeToString, utc
import datetime
import os
import types
@@ -791,7 +796,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource for
+ @param resource: the L{CalDAVResource} resource for
the shared collection. C{resource} must be a calendar/addressbook collection.)
"""
self.resource = resource
@@ -917,7 +922,7 @@
return Invite(*[str(item) if type(item) == types.UnicodeType else item for item in row])
-class SharedHomeMixin(object):
+class SharedHomeMixin(LinkFollowerMixIn):
"""
A mix-in for calendar/addressbook homes that defines the operations for manipulating a sharee's
set of shared calendars.
@@ -1146,7 +1151,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource for
+ @param resource: the L{CalDAVResource} resource for
the shared collection. C{resource} must be a calendar/addressbook home collection.)
"""
self.resource = resource
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/simpleresource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/simpleresource.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/simpleresource.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -14,13 +14,6 @@
# limitations under the License.
##
-from twext.web2.dav import davxml
-from twext.web2.dav.noneprops import NonePropertyStore
-from twisted.internet.defer import succeed
-from twistedcaldav.directory.util import NotFilePath
-from twistedcaldav.extensions import DAVFile
-from twistedcaldav.resource import CalDAVResource
-from twistedcaldav.static import CalDAVFile
"""
Implements a simple non-file resource.
@@ -31,61 +24,15 @@
"SimpleCalDAVResource",
]
-class SimpleResource (
- CalDAVResource,
- DAVFile,
-):
+from twext.web2.dav import davxml
+from twext.web2.dav.noneprops import NonePropertyStore
- allReadACL = davxml.ACL(
- # Read access for all users.
- davxml.ACE(
- davxml.Principal(davxml.All()),
- davxml.Grant(davxml.Privilege(davxml.Read())),
- davxml.Protected(),
- ),
- )
- authReadACL = davxml.ACL(
- # Read access for authenticated users.
- davxml.ACE(
- davxml.Principal(davxml.Authenticated()),
- davxml.Grant(davxml.Privilege(davxml.Read())),
- davxml.Protected(),
- ),
- )
+from twisted.internet.defer import succeed
- def __init__(self, principalCollections, isdir=False, defaultACL=authReadACL):
- """
- Make sure it is a collection.
- """
- CalDAVResource.__init__(self, principalCollections=principalCollections)
- DAVFile.__init__(self, NotFilePath(isfile=not isdir,isdir=isdir), principalCollections=principalCollections)
- self.defaultACL = defaultACL
+from twistedcaldav.resource import CalDAVResource
- def locateChild(self, req, segments):
- child = self.getChild(segments[0])
- if child is not None:
- return (child, segments[1:])
- return (None, ())
-
- def getChild(self, name):
- if name == "":
- return self
- else:
- return self.putChildren.get(name, None)
-
- def deadProperties(self):
- if not hasattr(self, "_dead_properties"):
- self._dead_properties = NonePropertyStore(self)
- return self._dead_properties
-
- def etag(self):
- return None
-
- def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
- return succeed(self.defaultACL)
-
-class SimpleCalDAVResource (
- CalDAVFile,
+class SimpleResource (
+ CalDAVResource,
):
allReadACL = davxml.ACL(
@@ -110,21 +57,12 @@
Make sure it is a collection.
"""
CalDAVResource.__init__(self, principalCollections=principalCollections)
- DAVFile.__init__(self, NotFilePath(isfile=not isdir,isdir=isdir), principalCollections=principalCollections)
+ self._isDir = isdir
self.defaultACL = defaultACL
- def locateChild(self, req, segments):
- child = self.getChild(segments[0])
- if child is not None:
- return (child, segments[1:])
- return (None, ())
+ def isCollection(self):
+ return self._isDir
- def getChild(self, name):
- if name == "":
- return self
- else:
- return self.putChildren.get(name, None)
-
def deadProperties(self):
if not hasattr(self, "_dead_properties"):
self._dead_properties = NonePropertyStore(self)
@@ -135,3 +73,5 @@
def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
return succeed(self.defaultACL)
+
+SimpleCalDAVResource = SimpleResource
Deleted: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -1,803 +0,0 @@
-# -*- test-case-name: twistedcaldav.test -*-
-##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-from twistedcaldav.index import Index
-
-"""
-CalDAV-aware static resources.
-"""
-
-__all__ = [
- "CalDAVFile",
- "AutoProvisioningFileMixIn",
- "DirectoryBackedAddressBookFile",
-]
-
-import os
-import errno
-from urlparse import urlsplit
-
-from twext.python.log import Logger
-
-from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue, maybeDeferred
-from twext.web2 import responsecode, http, http_headers
-from twext.web2.http import HTTPError, StatusResponse
-from twext.web2.dav import davxml
-from twext.web2.dav.fileop import mkcollection, rmdir
-from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.idav import IDAVResource
-from twext.web2.dav.resource import AccessDeniedError
-from twext.web2.dav.resource import davPrivilegeSet
-from twext.web2.dav.util import parentForURL, bindMethods
-
-from twistedcaldav import caldavxml
-from twistedcaldav import carddavxml
-from twistedcaldav.caldavxml import caldav_namespace
-from twistedcaldav.config import config
-from twistedcaldav.customxml import TwistedCalendarAccessProperty, TwistedScheduleMatchETags
-from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
-from twistedcaldav.extensions import DAVFile, CachingPropertyStore
-from twistedcaldav.linkresource import LinkFollowerMixIn
-
-from twistedcaldav.ical import Component as iComponent
-from twistedcaldav.ical import Property as iProperty
-from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource, isPseudoCalendarCollectionResource
-from twistedcaldav.resource import isAddressBookCollectionResource
-from twistedcaldav.datafilters.privateevents import PrivateEventFilter
-from twistedcaldav.directorybackedaddressbook import DirectoryBackedAddressBookResource
-from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn
-from twistedcaldav.vcardindex import AddressBookIndex
-
-log = Logger()
-
-class ReadOnlyResourceMixIn(object):
-
- def http_PUT (self, request): return responsecode.FORBIDDEN
- def http_COPY (self, request): return responsecode.FORBIDDEN
- def http_MOVE (self, request): return responsecode.FORBIDDEN
- def http_DELETE (self, request): return responsecode.FORBIDDEN
- def http_MKCOL (self, request): return responsecode.FORBIDDEN
-
- def http_MKCALENDAR(self, request):
- return ErrorResponse(
- responsecode.FORBIDDEN,
- (caldav_namespace, "calendar-collection-location-ok")
- )
-
-
-
-class CalDAVFile (LinkFollowerMixIn, CalDAVResource, DAVFile):
- """
- CalDAV-accessible L{DAVFile} resource.
- """
-# def __repr__(self):
-# if self.isCalendarCollection():
-# return "<%s (calendar collection): %s>" % (self.__class__.__name__, self.fp.path)
-# else:
-# return super(CalDAVFile, self).__repr__()
-
- def __eq__(self, other):
- if not isinstance(other, CalDAVFile):
- return False
- return self.fp.path == other.fp.path
-
- def checkPreconditions(self, request):
- """
- We override the base class to handle the special implicit scheduling weak ETag behavior
- for compatibility with old clients using If-Match.
- """
-
- if config.Scheduling.CalDAV.ScheduleTagCompatibility:
-
- if self.exists() and self.hasDeadProperty(TwistedScheduleMatchETags):
- etags = self.readDeadProperty(TwistedScheduleMatchETags).children
- if len(etags) > 1:
- # This is almost verbatim from twext.web2.static.checkPreconditions
- if request.method not in ("GET", "HEAD"):
-
- # Loop over each tag and succeed if any one matches, else re-raise last exception
- exists = self.exists()
- last_modified = self.lastModified()
- last_exception = None
- for etag in etags:
- try:
- http.checkPreconditions(
- request,
- entityExists = exists,
- etag = http_headers.ETag(etag),
- lastModified = last_modified,
- )
- except HTTPError, e:
- last_exception = e
- else:
- break
- else:
- if last_exception:
- raise last_exception
-
- # Check per-method preconditions
- method = getattr(self, "preconditions_" + request.method, None)
- if method:
- response = maybeDeferred(method, request)
- response.addCallback(lambda _: request)
- return response
- else:
- return None
-
- return super(CalDAVFile, self).checkPreconditions(request)
-
- def deadProperties(self, caching=True):
- if not hasattr(self, "_dead_properties"):
- # FIXME: this code should actually be dead, as the property store
- # should be initialized as part of the traversal process.
-
- # Get the property store from super
- deadProperties = super(CalDAVFile, self).deadProperties()
-
- if caching:
- # Wrap the property store in a memory store
- deadProperties = CachingPropertyStore(deadProperties)
-
- self._dead_properties = deadProperties
-
- return self._dead_properties
-
- ##
- # CalDAV
- ##
-
- def createCalendar(self, request):
- """
- External API for creating a calendar. Verify that the parent is a
- collection, exists, is I{not} a calendar collection; that this resource
- does not yet exist, then create it.
-
- @param request: the request used to look up parent resources to
- validate.
-
- @type request: L{twext.web2.iweb.IRequest}
-
- @return: a deferred that fires when a calendar collection has been
- created in this resource.
- """
- if self.fp.exists():
- log.err("Attempt to create collection where file exists: %s" % (self.fp.path,))
- raise HTTPError(StatusResponse(responsecode.NOT_ALLOWED, "File exists"))
-
- # newStore guarantees that we always have a parent calendar home
- #if not self.fp.parent().isdir():
- # log.err("Attempt to create collection with no parent: %s" % (self.fp.path,))
- # raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection"))
-
- #
- # Verify that no parent collection is a calendar also
- #
- log.msg("Creating calendar collection %s" % (self,))
-
- def _defer(parent):
- if parent is not None:
- log.err("Cannot create a calendar collection within a calendar collection %s" % (parent,))
- raise HTTPError(ErrorResponse(
- responsecode.FORBIDDEN,
- (caldavxml.caldav_namespace, "calendar-collection-location-ok")
- ))
-
- return self.createCalendarCollection()
-
- parent = self._checkParents(request, isPseudoCalendarCollectionResource)
- parent.addCallback(_defer)
- return parent
-
-
- def createCalendarCollection(self):
- """
- Internal API for creating a calendar collection.
-
- This will immediately create the collection without performing any
- verification. For the normal API, see L{CalDAVFile.createCalendar}.
-
- The default behavior is to return a failing Deferred; for a working
- implementation, see L{twistedcaldav.legacy}.
-
- @return: a L{Deferred} which fires when the underlying collection has
- actually been created.
- """
- return fail(NotImplementedError())
-
-
- def createSpecialCollection(self, resourceType=None):
- #
- # Create the collection once we know it is safe to do so
- #
- def onCollection(status):
- if status != responsecode.CREATED:
- raise HTTPError(status)
-
- self.writeDeadProperty(resourceType)
- return status
-
- def onError(f):
- try:
- rmdir(self.fp)
- except Exception, e:
- log.err("Unable to clean up after failed MKCOL (special resource type: %s): %s" % (e, resourceType,))
- return f
-
- d = mkcollection(self.fp)
- if resourceType is not None:
- d.addCallback(onCollection)
- d.addErrback(onError)
- return d
-
- @inlineCallbacks
- def iCalendarRolledup(self, request):
- if self.isPseudoCalendarCollection():
-
-
-# FIXME: move cache implementation!
- # Determine the cache key
-# isvirt = self.isVirtualShare()
-# if isvirt:
-# principal = (yield self.resourceOwnerPrincipal(request))
-# if principal:
-# cacheKey = principal.principalUID()
-# else:
-# cacheKey = "unknown"
-# else:
-# isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
-# cacheKey = "owner" if isowner else "notowner"
-
- # Now check for a cached .ics
-# rolled = self.fp.child(".subscriptions")
-# if not rolled.exists():
-# try:
-# rolled.makedirs()
-# except IOError, e:
-# log.err("Unable to create internet calendar subscription cache directory: %s because of: %s" % (rolled.path, e,))
-# raise HTTPError(ErrorResponse(responsecode.INTERNAL_SERVER_ERROR))
-# cached = rolled.child(cacheKey)
-# if cached.exists():
-# try:
-# cachedData = cached.open().read()
-# except IOError, e:
-# log.err("Unable to open or read internet calendar subscription cache file: %s because of: %s" % (cached.path, e,))
-# else:
-# # Check the cache token
-# token, data = cachedData.split("\r\n", 1)
-# if token == self.getSyncToken():
-# returnValue(data)
-
- # Generate a monolithic calendar
- calendar = iComponent("VCALENDAR")
- calendar.addProperty(iProperty("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 the checkPrivileges on each child.
- filteredaces = (yield self.inheritedACEsforChildren(request))
-
- tzids = set()
- isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
- accessPrincipal = (yield self.resourceOwnerPrincipal(request))
-
- for name, uid, type in self.index().bruteForceSearch(): #@UnusedVariable
- try:
- child = yield request.locateChildResource(self, name)
- child = IDAVResource(child)
- except TypeError:
- child = None
-
- if child is not None:
- # Check privileges of child - skip if access denied
- try:
- yield child.checkPrivileges(request, (davxml.Read(),), inherited_aces=filteredaces)
- except AccessDeniedError:
- continue
-
- # Get the access filtered view of the data
- caldata = child.iCalendarTextFiltered(isowner, accessPrincipal.principalUID() if accessPrincipal else "")
- try:
- subcalendar = iComponent.fromString(caldata)
- except ValueError:
- continue
- assert subcalendar.name() == "VCALENDAR"
-
- for component in subcalendar.subcomponents():
-
- # Only insert VTIMEZONEs once
- if component.name() == "VTIMEZONE":
- tzid = component.propertyValue("TZID")
- if tzid in tzids:
- continue
- tzids.add(tzid)
-
- calendar.addComponent(component)
-
- # Cache the data
- data = str(calendar)
- data = self.getSyncToken() + "\r\n" + data
-# try:
-# cached.open(mode='w').write(data)
-# except IOError, e:
-# log.err("Unable to open or write internet calendar subscription cache file: %s because of: %s" % (cached.path, e,))
-
- returnValue(calendar)
-
- raise HTTPError(ErrorResponse(responsecode.BAD_REQUEST))
-
- def iCalendarTextFiltered(self, isowner, accessUID=None):
- try:
- access = self.readDeadProperty(TwistedCalendarAccessProperty)
- except HTTPError:
- access = None
-
- # Now "filter" the resource calendar data
- caldata = PrivateEventFilter(access, isowner).filter(self.iCalendarText())
- if accessUID:
- caldata = PerUserDataFilter(accessUID).filter(caldata)
- return str(caldata)
-
- def iCalendarText(self, name=None):
- if self.isPseudoCalendarCollection():
- if name is None:
- return str(self.iCalendar())
-
- try:
- calendar_file = self.fp.child(name).open()
- except IOError, e:
- if e[0] == errno.ENOENT: return None
- raise
-
- elif self.isCollection():
- return None
-
- else:
- if name is not None:
- raise AssertionError("name must be None for non-collection calendar resource")
-
- calendar_file = self.fp.open()
-
- # FIXME: This is blocking I/O
- try:
- calendar_data = calendar_file.read()
- finally:
- calendar_file.close()
-
- return calendar_data
-
- def createAddressBook(self, request):
- """
- External API for creating an addressbook. Verify that the parent is a
- collection, exists, is I{not} an addressbook collection; that this resource
- does not yet exist, then create it.
-
- @param request: the request used to look up parent resources to
- validate.
-
- @type request: L{twext.web2.iweb.IRequest}
-
- @return: a deferred that fires when an addressbook collection has been
- created in this resource.
- """
- #
- # request object is required because we need to validate against parent
- # resources, and we need the request in order to locate the parents.
- #
-
- if self.fp.exists():
- log.err("Attempt to create collection where file exists: %s" % (self.fp.path,))
- raise HTTPError(StatusResponse(responsecode.NOT_ALLOWED, "File exists"))
-
- # newStore guarantees that we always have a parent calendar home
- #if not os.path.isdir(os.path.dirname(self.fp.path)):
- # log.err("Attempt to create collection with no parent: %s" % (self.fp.path,))
- # raise HTTPError(StatusResponse(responsecode.CONFLICT, "No parent collection"))
-
- #
- # Verify that no parent collection is a calendar also
- #
- log.msg("Creating address book collection %s" % (self,))
-
- def _defer(parent):
- if parent is not None:
- log.err("Cannot create an address book collection within an address book collection %s" % (parent,))
- raise HTTPError(ErrorResponse(
- responsecode.FORBIDDEN,
- (carddavxml.carddav_namespace, "addressbook-collection-location-ok")
- ))
-
- return self.createAddressBookCollection()
-
- parent = self._checkParents(request, isAddressBookCollectionResource)
- parent.addCallback(_defer)
- return parent
-
- def createAddressBookCollection(self):
- """
- Internal API for creating an addressbook collection.
-
- This will immediately create the collection without performing any
- verification. For the normal API, see L{CalDAVFile.createAddressBook}.
-
- The default behavior is to return a failing Deferred; for a working
- implementation, see L{twistedcaldav.legacy}.
-
- @return: a L{Deferred} which fires when the underlying collection has
- actually been created.
- """
- return fail(NotImplementedError())
-
- @inlineCallbacks
- def vCardRolledup(self, request):
- # TODO: just catenate all the vCards together
- yield fail(HTTPError((ErrorResponse(responsecode.BAD_REQUEST))))
-
- def vCardText(self, name=None):
- if self.isAddressBookCollection():
- if name is None:
- return str(self.vCard())
-
- try:
- vcard_file = self.fp.child(name).open()
- except IOError, e:
- if e[0] == errno.ENOENT: return None
- raise
-
- elif self.isCollection():
- return None
-
- else:
- if name is not None:
- raise AssertionError("name must be None for non-collection vcard resource")
-
- vcard_file = self.fp.open()
-
- # FIXME: This is blocking I/O
- try:
- vcard_data = vcard_file.read()
- finally:
- vcard_file.close()
-
- return vcard_data
-
- def vCardXML(self, name=None):
- return carddavxml.AddressData.fromAddressData(self.vCardText(name))
-
- def supportedPrivileges(self, request):
- # read-free-busy support on calendar collection and calendar object resources
- if self.isCollection():
- return succeed(calendarPrivilegeSet)
- else:
- def gotParent(parent):
- if parent and isCalendarCollectionResource(parent):
- return succeed(calendarPrivilegeSet)
- else:
- return super(CalDAVFile, self).supportedPrivileges(request)
-
- d = self.locateParent(request, request.urlForResource(self))
- d.addCallback(gotParent)
- return d
-
- return super(CalDAVFile, self).supportedPrivileges(request)
-
- ##
- # Public additions
- ##
-
- def index(self):
- """
- Obtains the index for a calendar collection resource.
- @return: the index object for this resource.
- @raise AssertionError: if this resource is not a calendar collection
- resource.
- """
- if self.isAddressBookCollection():
- return AddressBookIndex(self)
- else:
- return Index(self)
-
- def listChildren(self):
- return [
- child for child in super(CalDAVFile, self).listChildren()
- if not child.startswith(".")
- ]
-
- def createSimilarFile(self, path):
- if self.comparePath(path):
- return self
-
- similar = super(CalDAVFile, self).createSimilarFile(path)
-
- if isCalendarCollectionResource(self):
- raise RuntimeError("Calendar collection resources should really "
- "be represented by a different class.")
-
- return similar
-
- ##
- # Quota
- ##
-
- def quotaSize(self, request):
- """
- Get the size of this resource.
- TODO: Take into account size of dead-properties. Does stat include xattrs size?
-
- @return: an L{Deferred} with a C{int} result containing the size of the resource.
- """
- if self.isCollection():
- @inlineCallbacks
- def walktree(top):
- """
- Recursively descend the directory tree rooted at top,
- calling the callback function for each regular file
-
- @param top: L{FilePath} for the directory to walk.
- """
-
- total = 0
- for f in top.listdir():
-
- # Ignore the database
- if f.startswith("."):
- continue
-
- child = top.child(f)
- if child.isdir():
- # It's a directory, recurse into it
- total += yield walktree(child)
- elif child.isfile():
- # It's a file, call the callback function
- total += child.getsize()
- else:
- # Unknown file type, print a message
- pass
-
- returnValue(total)
-
- return walktree(self.fp)
- else:
- return succeed(self.fp.getsize())
-
- ##
- # Utilities
- ##
-
- @staticmethod
- def _isChildURI(request, uri, immediateChild=True):
- """
- Verify that the supplied URI represents a resource that is a child
- of the request resource.
- @param request: the request currently in progress
- @param uri: the URI to test
- @return: True if the supplied URI is a child resource
- False if not
- """
- if uri is None: return False
-
- #
- # Parse the URI
- #
-
- (scheme, host, path, query, fragment) = urlsplit(uri) #@UnusedVariable
-
- # Request hostname and child uri hostname have to be the same.
- if host and host != request.headers.getHeader("host"):
- return False
-
- # Child URI must start with request uri text.
- parent = request.uri
- if not parent.endswith("/"):
- parent += "/"
-
- return path.startswith(parent) and (len(path) > len(parent)) and (not immediateChild or (path.find("/", len(parent)) == -1))
-
- @inlineCallbacks
- def _checkParents(self, request, test):
- """
- @param request: the request being processed.
- @param test: a callable
- @return: the closest parent for this resource using the request URI from
- the given request for which C{test(parent)} evaluates to a true
- value, or C{None} if no parent matches.
- """
- parent = self
- parent_uri = request.uri
-
- while True:
- parent_uri = parentForURL(parent_uri)
- if not parent_uri: break
-
- parent = yield request.locateResource(parent_uri)
-
- if test(parent):
- returnValue(parent)
-
-class AutoProvisioningFileMixIn (LinkFollowerMixIn, AutoProvisioningResourceMixIn):
- def provision(self):
- self.provisionFile()
- return super(AutoProvisioningFileMixIn, self).provision()
-
-
- def provisionFile(self):
- if hasattr(self, "_provisioned_file"):
- return False
- else:
- self._provisioned_file = True
-
- # If the file already exists we can just exit here - there is no need to go further
- if self.fp.exists():
- return False
-
- # At this point the original FilePath did not indicate an existing file, but we should
- # recheck it to see if some other request sneaked in and already created/provisioned it
-
- fp = self.fp
-
- fp.restat(False)
- if fp.exists():
- return False
-
- log.msg("Provisioning file: %s" % (self,))
-
- if hasattr(self, "parent"):
- parent = self.parent
- if not parent.exists() and isinstance(parent, AutoProvisioningFileMixIn):
- parent.provision()
-
- assert parent.exists(), "Parent %s of %s does not exist" % (parent, self)
- assert parent.isCollection(), "Parent %s of %s is not a collection" % (parent, self)
-
- if self.isCollection():
- try:
- fp.makedirs()
- except OSError:
- # It's possible someone else created the directory in the meantime...
- # Check our status again, and re-raise if we're not a collection.
- if not self.isCollection():
- raise
- fp.changed()
- else:
- fp.open("w").close()
- fp.changed()
-
- return True
-
- def _initTypeAndEncoding(self):
-
- # Handle cases not covered by getTypeAndEncoding()
- if self.isCollection():
- self._type = "httpd/unix-directory"
- else:
- super(AutoProvisioningFileMixIn, self)._initTypeAndEncoding()
-
-
-class DirectoryBackedAddressBookFile (ReadOnlyResourceMixIn, DirectoryBackedAddressBookResource, CalDAVFile):
- """
- Directory-backed address book, supporting directory vcard search.
- """
- def __init__(self, path, principalCollections):
- CalDAVFile.__init__(self, path, principalCollections=principalCollections)
- DirectoryBackedAddressBookResource.__init__(self)
-
- # create with permissions, similar to CardDAVOptions in tap.py
- # FIXME: /Directory does not need to be in file system unless debug-only caching options are used
- try:
- os.mkdir(path)
- os.chmod(path, 0750)
- if config.UserName and config.GroupName:
- import pwd
- import grp
- uid = pwd.getpwnam(config.UserName)[2]
- gid = grp.getgrnam(config.GroupName)[2]
- os.chown(path, uid, gid)
-
- log.msg("Created %s" % (path,))
-
- except (OSError,), e:
- # this is caused by multiprocessor race and is harmless
- if e.errno != errno.EEXIST:
- raise
-
-
- def getChild(self, name):
-
- if name is "":
- return self
- else:
- from twistedcaldav.simpleresource import SimpleCalDAVResource
- return SimpleCalDAVResource(principalCollections=self.principalCollections())
-
- def createSimilarFile(self, path):
- if self.comparePath(path):
- return self
- else:
- from twistedcaldav.simpleresource import SimpleCalDAVResource
- return SimpleCalDAVResource(principalCollections=self.principalCollections())
-
-##
-# Utilities
-##
-
-def locateExistingChild(resource, request, segments):
- """
- This C{locateChild()} implementation fails to find children if C{getChild()}
- doesn't return one.
- """
- # If getChild() finds a child resource, return it
- child = resource.getChild(segments[0])
- if child is not None:
- return (child, segments[1:])
-
- # Otherwise, there is no child
- return (None, ())
-
-def _calendarPrivilegeSet ():
- edited = False
-
- top_supported_privileges = []
-
- for supported_privilege in davPrivilegeSet.childrenOfType(davxml.SupportedPrivilege):
- all_privilege = supported_privilege.childOfType(davxml.Privilege)
- if isinstance(all_privilege.children[0], davxml.All):
- all_description = supported_privilege.childOfType(davxml.Description)
- all_supported_privileges = []
- for all_supported_privilege in supported_privilege.childrenOfType(davxml.SupportedPrivilege):
- read_privilege = all_supported_privilege.childOfType(davxml.Privilege)
- if isinstance(read_privilege.children[0], davxml.Read):
- read_description = all_supported_privilege.childOfType(davxml.Description)
- read_supported_privileges = list(all_supported_privilege.childrenOfType(davxml.SupportedPrivilege))
- read_supported_privileges.append(
- davxml.SupportedPrivilege(
- davxml.Privilege(caldavxml.ReadFreeBusy()),
- davxml.Description("allow free busy report query", **{"xml:lang": "en"}),
- )
- )
- all_supported_privileges.append(
- davxml.SupportedPrivilege(read_privilege, read_description, *read_supported_privileges)
- )
- edited = True
- else:
- all_supported_privileges.append(all_supported_privilege)
- top_supported_privileges.append(
- davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
- )
- else:
- top_supported_privileges.append(supported_privilege)
-
- assert edited, "Structure of davPrivilegeSet changed in a way that I don't know how to extend for calendarPrivilegeSet"
-
- return davxml.SupportedPrivilegeSet(*top_supported_privileges)
-
-calendarPrivilegeSet = _calendarPrivilegeSet()
-
-##
-# Attach methods
-##
-
-import twistedcaldav.method
-
-bindMethods(twistedcaldav.method, CalDAVFile)
-
-# FIXME: Little bit of a circular dependency here...
-twistedcaldav.method.acl.CalDAVFile = CalDAVFile
-twistedcaldav.method.copymove.CalDAVFile = CalDAVFile
-twistedcaldav.method.delete.CalDAVFile = CalDAVFile
-twistedcaldav.method.get.CalDAVFile = CalDAVFile
-twistedcaldav.method.mkcol.CalDAVFile = CalDAVFile
-twistedcaldav.method.propfind.CalDAVFile = CalDAVFile
-twistedcaldav.method.put.CalDAVFile = CalDAVFile
-
-
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -14,8 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from twistedcaldav.schedule import ScheduleInboxResource
-from twistedcaldav.extensions import DAVResourceWithChildrenMixin
"""
Wrappers to translate between the APIs in L{txcaldav.icalendarstore} and
@@ -28,40 +26,37 @@
from twisted.internet.defer import succeed, inlineCallbacks, returnValue
from twisted.internet.protocol import Protocol
+from twisted.python.log import err as logDefaultException
from twisted.python.util import FancyEqMixin
from twext.python import vcomponent
-from twext.python.filepath import CachingFilePath as FilePath
from twext.python.log import Logger
-from twext.web2.http_headers import ETag, MimeType
+from twext.web2.dav import davxml
+from twext.web2.dav.element.base import dav_namespace
from twext.web2.dav.http import ErrorResponse, ResponseQueue
-from twext.web2.dav.element.base import dav_namespace
-from twext.web2.responsecode import (
- FORBIDDEN, NO_CONTENT, NOT_FOUND, CREATED, CONFLICT, PRECONDITION_FAILED,
- BAD_REQUEST, OK, NOT_IMPLEMENTED, NOT_ALLOWED)
-from twext.web2.dav import davxml
from twext.web2.dav.resource import TwistedACLInheritable
from twext.web2.dav.util import parentForURL, allDataFromStream, joinURL, \
davXMLFromStream
from twext.web2.http import HTTPError, StatusResponse, Response
+from twext.web2.http_headers import ETag, MimeType
+from twext.web2.responsecode import (
+ FORBIDDEN, NO_CONTENT, NOT_FOUND, CREATED, CONFLICT, PRECONDITION_FAILED,
+ BAD_REQUEST, OK, NOT_IMPLEMENTED, NOT_ALLOWED
+)
from twext.web2.stream import ProducerStream, readStream, MemoryStream
-from twistedcaldav.static import CalDAVFile
-from twistedcaldav.vcard import Component as VCard
-from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource
-
-from txdav.common.icommondatastore import NoSuchObjectResourceError, \
- InternalDataStoreError
-from txdav.propertystore.base import PropertyName
-
from twistedcaldav.caldavxml import ScheduleTag, caldav_namespace
+from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.notifications import NotificationCollectionResource,\
NotificationResource
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource
+from twistedcaldav.schedule import ScheduleInboxResource
from twistedcaldav.scheduling.implicit import ImplicitScheduler
+from twistedcaldav.vcard import Component as VCard
-from twisted.python.log import err as logDefaultException
+from txdav.common.icommondatastore import NoSuchObjectResourceError
+from txdav.propertystore.base import PropertyName
log = Logger()
@@ -159,7 +154,47 @@
return wrap
+class _NewStoreFileMetaDataHelper(object):
+ def name(self):
+ return self._newStoreObject.name()
+
+ def etag(self):
+ # FIXME: far too slow to be used for real, but I needed something to
+ # placate the etag computation in the case where the file doesn't exist
+ # yet (an uncommitted transaction creating this calendar file)
+
+ # FIXME: direct tests
+ try:
+ md5 = self._newStoreObject.md5()
+ if md5:
+ return ETag(md5)
+ else:
+ return ETag(
+ hashlib.new("md5", self.text()).hexdigest(),
+ weak=False
+ )
+ except NoSuchObjectResourceError:
+ # FIXME: a workaround for the fact that DELETE still rudely vanishes
+ # the calendar object out from underneath the store, and doesn't
+ # call storeRemove.
+ return None
+
+ 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()
+
class _CalendarChildHelper(object):
"""
Methods for things which are like calendars.
@@ -213,21 +248,26 @@
def makeChild(self, name):
"""
- Create a L{CalendarObjectFile} or L{ProtoCalendarObjectFile} based on a
+ Create a L{CalendarObjectResource} or L{ProtoCalendarObjectResource} based on a
path object.
"""
newStoreObject = self._newStoreCalendar.calendarObjectWithName(name)
if newStoreObject is not None:
- similar = CalendarObjectFile(newStoreObject, newStoreObject._path,
- principalCollections=self._principalCollections)
+ similar = CalendarObjectResource(
+ newStoreObject,
+ principalCollections=self._principalCollections
+ )
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 = ProtoCalendarObjectFile(self._newStoreCalendar, self._newStoreCalendar._path.child(name),
- principalCollections=self._principalCollections)
+ similar = ProtoCalendarObjectResource(
+ self._newStoreCalendar,
+ name,
+ principalCollections=self._principalCollections
+ )
# FIXME: tests should be failing without this line.
# Specifically, http_PUT won't be committing its transaction properly.
@@ -240,7 +280,7 @@
"""
children = set(self.putChildren.keys())
children.update(self._newStoreCalendar.listCalendarObjects())
- return children
+ return sorted(children)
def quotaSize(self, request):
@@ -355,8 +395,6 @@
l.append(everyObject.dropboxID())
return l
-
-
class NoDropboxHere(_GetChildHelper):
def isCollection(self):
@@ -535,14 +573,16 @@
return CREATED
return readStream(request.stream, t.write).addCallback(done)
+ http_MKCOL = None
+ http_MKCALENDAR = None
-class CalendarAttachment(_GetChildHelper):
+class CalendarAttachment(_NewStoreFileMetaDataHelper, _GetChildHelper):
def __init__(self, calendarObject, attachment, **kw):
super(CalendarAttachment, self).__init__(**kw)
self._newStoreCalendarObject = calendarObject
- self._newStoreAttachment = attachment
+ self._newStoreAttachment = self._newStoreObject = attachment
def etag(self):
@@ -597,13 +637,15 @@
self.__class__ = ProtoCalendarAttachment
return NO_CONTENT
+ http_MKCOL = None
+ http_MKCALENDAR = None
def isCollection(self):
return False
-class CalendarCollectionResource(_CalendarChildHelper, CalDAVResource, DAVResourceWithChildrenMixin):
+class CalendarCollectionResource(_CalendarChildHelper, CalDAVResource):
"""
Wrapper around a L{txcaldav.icalendar.ICalendar}.
"""
@@ -614,10 +656,12 @@
and the arguments required for L{CalDAVResource}.
"""
super(CalendarCollectionResource, self).__init__(*args, **kw)
- DAVResourceWithChildrenMixin.__init__(self)
self._initializeWithCalendar(calendar, home)
+ def name(self):
+ return self._newStoreCalendar.name()
+
def isCollection(self):
return True
@@ -766,7 +810,7 @@
returnValue(FORBIDDEN)
destination = yield request.locateResource(destinationURI)
# FIXME: should really use something other than 'fp' attribute.
- basename = destination.fp.basename()
+ basename = destination.name()
calendar = self._newStoreCalendar
calendar.rename(basename)
CalendarCollectionResource.transform(destination, calendar,
@@ -787,14 +831,15 @@
def http_PUT(self, request):
return CONFLICT
+ def isCollection(self):
+ return False
-
class ProtoCalendarCollectionResource(CalDAVResource):
"""
A resource representing a calendar collection which hasn't yet been created.
"""
- def __init__(self, home, *args, **kw):
+ def __init__(self, home, name, *args, **kw):
"""
A placeholder resource for a calendar collection which does not yet
exist, but will become a L{CalendarCollectionResource}.
@@ -804,8 +849,9 @@
@type home: L{txcaldav.icalendarstore.ICalendarHome}
"""
+ super(ProtoCalendarCollectionResource, self).__init__(*args, **kw)
self._newStoreParentHome = home
- super(ProtoCalendarCollectionResource, self).__init__(*args, **kw)
+ self._name = name
def isCollection(self):
@@ -833,10 +879,9 @@
"""
d = succeed(CREATED)
- calendarName = self.fp.basename()
- self._newStoreParentHome.createCalendarWithName(calendarName)
+ self._newStoreParentHome.createCalendarWithName(self._name)
newStoreCalendar = self._newStoreParentHome.calendarWithName(
- calendarName
+ self._name
)
CalendarCollectionResource.transform(
self, newStoreCalendar, self._newStoreParentHome
@@ -849,6 +894,9 @@
return False
+ def name(self):
+ return self._name
+
def provision(self):
"""
This resource should do nothing if it's provisioned.
@@ -862,7 +910,7 @@
-class CalendarObjectFile(CalDAVFile, FancyEqMixin):
+class CalendarObjectResource(_NewStoreFileMetaDataHelper, CalDAVResource, FancyEqMixin):
"""
A resource wrapping a calendar object.
"""
@@ -871,19 +919,15 @@
def __init__(self, calendarObject, *args, **kw):
"""
- Construct a L{CalendarObjectFile} from an L{ICalendarObject}.
+ Construct a L{CalendarObjectResource} from an L{ICalendarObject}.
@param calendarObject: The storage for the calendar object.
@type calendarObject: L{txcaldav.icalendarstore.ICalendarObject}
"""
- super(CalendarObjectFile, self).__init__(*args, **kw)
+ super(CalendarObjectResource, self).__init__(*args, **kw)
self._initializeWithObject(calendarObject)
- def isCollection(self):
- return False
-
-
def inNewTransaction(self, request):
"""
Implicit auto-replies need to span multiple transactions. Clean out
@@ -910,41 +954,15 @@
return txn
+ def isCollection(self):
+ return False
+
+
def exists(self):
# FIXME: Tests
return True
- def name(self):
- return self._newStoreObject.name()
-
-
- def etag(self):
- # FIXME: far too slow to be used for real, but I needed something to
- # placate the etag computation in the case where the file doesn't exist
- # yet (an uncommited transaction creating this calendar file)
-
- # FIXME: direct tests
- try:
- md5 = self._newStoreObject.md5()
- if md5:
- return ETag(md5)
- else:
- return ETag(
- hashlib.new("md5", self.iCalendarText()).hexdigest(),
- weak=False
- )
- except (NoSuchObjectResourceError, InternalDataStoreError):
- # FIXME: a workaround for the fact that DELETE still rudely vanishes
- # the calendar object out from underneath the store, and doesn't
- # call storeRemove.
- return None
-
-
- def newStoreProperties(self):
- return self._newStoreObject.properties()
-
-
def quotaSize(self, request):
# FIXME: tests
return succeed(len(self._newStoreObject.iCalendarText()))
@@ -1071,7 +1089,7 @@
# FIXME: clean this up with a 'transform' method
self._newStoreParentCalendar = storeCalendar
del self._newStoreObject
- self.__class__ = ProtoCalendarObjectFile
+ self.__class__ = ProtoCalendarObjectResource
# Adjust quota
if myquota is not None:
@@ -1108,13 +1126,14 @@
-class ProtoCalendarObjectFile(CalDAVFile, FancyEqMixin):
+class ProtoCalendarObjectResource(CalDAVResource, FancyEqMixin):
compareAttributes = '_newStoreParentCalendar'.split()
- def __init__(self, parentCalendar, *a, **kw):
- super(ProtoCalendarObjectFile, self).__init__(*a, **kw)
+ def __init__(self, parentCalendar, name, *a, **kw):
+ super(ProtoCalendarObjectResource, self).__init__(*a, **kw)
self._newStoreParentCalendar = parentCalendar
+ self._name = name
@inlineCallbacks
@@ -1124,9 +1143,9 @@
(yield allDataFromStream(stream))
)
self._newStoreParentCalendar.createCalendarObjectWithName(
- self.fp.basename(), component
+ self.name(), component
)
- CalendarObjectFile.transform(self, self._newStoreParentCalendar.calendarObjectWithName(self.fp.basename()))
+ CalendarObjectResource.transform(self, self._newStoreParentCalendar.calendarObjectWithName(self.name()))
returnValue(CREATED)
@@ -1139,7 +1158,7 @@
def name(self):
- return self.fp.basename()
+ return self._name
def quotaSize(self, request):
# FIXME: tests, workingness
@@ -1198,20 +1217,25 @@
def makeChild(self, name):
"""
- Create a L{AddressBookObjectFile} or L{ProtoAddressBookObjectFile} based on a
+ Create a L{AddressBookObjectResource} or L{ProtoAddressBookObjectResource} based on a
path object.
"""
newStoreObject = self._newStoreAddressBook.addressbookObjectWithName(name)
if newStoreObject is not None:
- similar = AddressBookObjectFile(newStoreObject, newStoreObject._path,
- principalCollections=self._principalCollections)
+ similar = AddressBookObjectResource(
+ newStoreObject,
+ principalCollections=self._principalCollections
+ )
else:
# FIXME: creation in http_PUT should talk to a specific resource
# type; this is the domain of StoreAddressBookObjectResource.
# similar = ProtoAddressBookObjectFile(self._newStoreAddressBook, path)
- similar = ProtoAddressBookObjectFile(self._newStoreAddressBook, self._newStoreAddressBook._path.child(name),
- principalCollections=self._principalCollections)
+ similar = ProtoAddressBookObjectResource(
+ self._newStoreAddressBook,
+ name,
+ principalCollections=self._principalCollections
+ )
# FIXME: tests should be failing without this line.
# Specifically, http_PUT won't be committing its transaction properly.
@@ -1224,7 +1248,7 @@
"""
children = set(self.putChildren.keys())
children.update(self._newStoreAddressBook.listAddressbookObjects())
- return children
+ return sorted(children)
@@ -1234,7 +1258,7 @@
-class AddressBookCollectionResource(_AddressBookChildHelper, CalDAVResource, DAVResourceWithChildrenMixin):
+class AddressBookCollectionResource(_AddressBookChildHelper, CalDAVResource):
"""
Wrapper around a L{txcarddav.iaddressbook.IAddressBook}.
"""
@@ -1245,10 +1269,12 @@
and the arguments required for L{CalDAVResource}.
"""
super(AddressBookCollectionResource, self).__init__(*args, **kw)
- DAVResourceWithChildrenMixin.__init__(self)
self._initializeWithAddressBook(addressbook, home)
+ def name(self):
+ return self._newStoreAddressBook.name()
+
def isCollection(self):
return True
@@ -1377,7 +1403,7 @@
returnValue(FORBIDDEN)
destination = yield request.locateResource(destinationURI)
# FIXME: should really use something other than 'fp' attribute.
- basename = destination.fp.basename()
+ basename = destination.name()
addressbook = self._newStoreAddressBook
addressbook.rename(basename)
AddressBookCollectionResource.transform(destination, addressbook,
@@ -1393,7 +1419,7 @@
A resource representing an addressbook collection which hasn't yet been created.
"""
- def __init__(self, home, *args, **kw):
+ def __init__(self, home, name, *args, **kw):
"""
A placeholder resource for an addressbook collection which does not yet
exist, but will become a L{AddressBookCollectionResource}.
@@ -1403,8 +1429,9 @@
@type home: L{txcarddav.iaddressbookstore.IAddressBookHome}
"""
+ super(ProtoAddressBookCollectionResource, self).__init__(*args, **kw)
self._newStoreParentHome = home
- super(ProtoAddressBookCollectionResource, self).__init__(*args, **kw)
+ self._name = name
def isCollection(self):
@@ -1434,10 +1461,9 @@
"""
d = succeed(CREATED)
- Name = self.fp.basename()
- self._newStoreParentHome.createAddressBookWithName(Name)
+ self._newStoreParentHome.createAddressBookWithName(self._name)
newStoreAddressBook = self._newStoreParentHome.addressbookWithName(
- Name
+ self._name
)
AddressBookCollectionResource.transform(
self, newStoreAddressBook, self._newStoreParentHome
@@ -1450,6 +1476,9 @@
return False
+ def name(self):
+ return self._name
+
def provision(self):
"""
This resource should do nothing if it's provisioned.
@@ -1475,7 +1504,7 @@
pass
-class AddressBookObjectFile(CalDAVFile, FancyEqMixin):
+class AddressBookObjectResource(_NewStoreFileMetaDataHelper, CalDAVResource, FancyEqMixin):
"""
A resource wrapping a addressbook object.
"""
@@ -1484,12 +1513,12 @@
def __init__(self, Object, *args, **kw):
"""
- Construct a L{AddressBookObjectFile} from an L{IAddressBookObject}.
+ Construct a L{AddressBookObjectResource} from an L{IAddressBookObject}.
@param Object: The storage for the addressbook object.
@type Object: L{txcarddav.iaddressbookstore.IAddressBookObject}
"""
- super(AddressBookObjectFile, self).__init__(*args, **kw)
+ super(AddressBookObjectResource, self).__init__(*args, **kw)
self._initializeWithObject(Object)
@@ -1508,7 +1537,7 @@
homeUID = self._newStoreObject._addressbook._addressbookHome.uid()
store = self._newStoreObject._transaction.store()
txn = store.newTransaction("new AB transaction for " + self._newStoreObject.name())
- newObject = (txn.HomeWithUID(homeUID)
+ newObject = (txn.addressbookHomeWithUID(homeUID)
.addressbookWithName(Name)
.addressbookObjectWithName(objectName))
request._newStoreTransaction = txn
@@ -1526,35 +1555,6 @@
return True
- def name(self):
- return self._newStoreObject.name()
-
- def etag(self):
- # FIXME: far too slow to be used for real, but I needed something to
- # placate the etag computation in the case where the file doesn't exist
- # yet (an uncommited transaction creating this addressbook file)
-
- # FIXME: direct tests
- try:
- md5 = self._newStoreObject.md5()
- if md5:
- return ETag(md5)
- else:
- return ETag(
- hashlib.new("md5", self.vCardText()).hexdigest(),
- weak=False
- )
- except (NoSuchObjectResourceError, InternalDataStoreError):
- # FIXME: a workaround for the fact that DELETE still rudely vanishes
- # the addressbook object out from underneath the store, and doesn't
- # call storeRemove.
- return None
-
-
- def newStoreProperties(self):
- return self._newStoreObject.properties()
-
-
def quotaSize(self, request):
# FIXME: tests
return succeed(len(self._newStoreObject.vCardText()))
@@ -1565,6 +1565,13 @@
return self._newStoreObject.vCardText()
+ def render(self, request):
+ output = self.vCardText()
+
+ response = Response(200, {}, output)
+ response.headers.setHeader("content-type", self.contentType())
+ return response
+
@requiresPermissions(fromParent=[davxml.Unbind()])
def http_DELETE(self, request):
"""
@@ -1607,7 +1614,7 @@
# FIXME: clean this up with a 'transform' method
self._newStoreParentAddressBook = storeAddressBook
del self._newStoreObject
- self.__class__ = ProtoAddressBookObjectFile
+ self.__class__ = ProtoAddressBookObjectResource
# Adjust quota
if myquota is not None:
@@ -1633,15 +1640,15 @@
-class ProtoAddressBookObjectFile(CalDAVFile, FancyEqMixin):
+class ProtoAddressBookObjectResource(CalDAVResource, FancyEqMixin):
compareAttributes = '_newStoreParentAddressBook'.split()
- def __init__(self, parentAddressBook, *a, **kw):
- super(ProtoAddressBookObjectFile, self).__init__(*a, **kw)
+ 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
@@ -1649,9 +1656,9 @@
(yield allDataFromStream(stream))
)
self._newStoreParentAddressBook.createAddressBookObjectWithName(
- self.fp.basename(), component
+ self.name(), component
)
- AddressBookObjectFile.transform(self, self._newStoreParentAddressBook.addressbookObjectWithName(self.fp.basename()))
+ AddressBookObjectResource.transform(self, self._newStoreParentAddressBook.addressbookObjectWithName(self.name()))
returnValue(CREATED)
@@ -1664,7 +1671,7 @@
def name(self):
- return self.fp.basename()
+ return self._name
def quotaSize(self, request):
# FIXME: tests, workingness
@@ -1835,7 +1842,7 @@
"""
d = succeed(CREATED)
- notificationName = self.fp.basename()
+ notificationName = self.name()
self._newStoreParentHome.createChildWithName(notificationName)
newStoreNotification = self._newStoreParentHome.childWithName(
notificationName
@@ -1871,7 +1878,7 @@
def __init__(self, notificationObject, *args, **kw):
"""
- Construct a L{CalendarObjectFile} from an L{ICalendarObject}.
+ Construct a L{CalendarObjectResource} from an L{ICalendarObject}.
@param calendarObject: The storage for the calendar object.
@type calendarObject: L{txcaldav.icalendarstore.ICalendarObject}
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/vcardindex.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/vcardindex.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/vcardindex.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -221,7 +221,7 @@
def __init__(self, resource):
"""
- @param resource: the L{twistedcaldav.static.CalDAVFile} resource to
+ @param resource: the L{CalDAVResource} resource to
index. C{resource} must be an addressbook collection (ie.
C{resource.isAddressBookCollection()} returns C{True}.)
"""
Modified: CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -281,7 +281,7 @@
def component(self):
if self._component is not None:
return self._component
- text = self.iCalendarText()
+ text = self.text()
try:
component = VComponent.fromString(text)
@@ -293,7 +293,7 @@
return component
- def iCalendarText(self):
+ def text(self):
if self._component is not None:
return str(self._component)
try:
@@ -319,6 +319,7 @@
)
return text
+ iCalendarText = text
def uid(self):
if not hasattr(self, "_uid"):
@@ -440,7 +441,7 @@
"""
self._attachment = attachment
self._contentType = contentType
- self._file = self._attachment._computePath().open("w")
+ self._file = self._attachment._path.open("w")
def write(self, data):
@@ -452,7 +453,7 @@
# FIXME: do anything
self._file.close()
- md5 = hashlib.md5(self._attachment._computePath().getContent()).hexdigest()
+ md5 = hashlib.md5(self._attachment._path.getContent()).hexdigest()
props = self._attachment.properties()
props[contentTypeKey] = GETContentType(generateContentType(self._contentType))
props[md5key] = TwistedGETContentMD5.fromString(md5)
@@ -491,7 +492,7 @@
return PropertyStore(
self._calendarObject._parentCollection._home.peruser_uid(),
self._calendarObject._parentCollection._home.uid(),
- self._computePath
+ lambda :self._path
)
@@ -502,12 +503,12 @@
# FIXME: makeConnection
# FIXME: actually stream
# FIMXE: connectionLost
- protocol.dataReceived(self._computePath().getContent())
+ protocol.dataReceived(self._path.getContent())
# FIXME: ConnectionDone, not NotImplementedError
protocol.connectionLost(Failure(NotImplementedError()))
-
- def _computePath(self):
+ @property
+ def _path(self):
dropboxPath = self._calendarObject._dropboxPath()
return dropboxPath.child(self.name())
Modified: CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -211,7 +211,7 @@
def component(self):
if self._component is not None:
return self._component
- text = self.vCardText()
+ text = self.text()
try:
component = VComponent.fromString(text)
@@ -223,7 +223,7 @@
return component
- def vCardText(self):
+ def text(self):
if self._component is not None:
return str(self._component)
try:
@@ -249,6 +249,7 @@
)
return text
+ vCardText = text
def uid(self):
if not hasattr(self, "_uid"):
Modified: CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py 2010-07-23 16:50:33 UTC (rev 5925)
+++ CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py 2010-07-23 17:06:51 UTC (rev 5926)
@@ -308,13 +308,13 @@
"""
Return a set of the names of the child resources.
"""
- return set(
+ return sorted(set(
[child.name() for child in self._newChildren.itervalues()]
) | set(
name
for name in self._path.listdir()
if not name.startswith(".")
- )
+ ))
def childWithName(self, name):
child = self._newChildren.get(name)
@@ -629,7 +629,7 @@
def _updateSyncToken(self, reset=False):
- # FIXME: add locking a-la CalDAVFile.bumpSyncToken
+ # FIXME: add locking a-la CalDAVResource.bumpSyncToken
# FIXME: tests for desired concurrency properties
ctag = PropertyName.fromString(GETCTag.sname())
props = self.properties()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100723/ad04e355/attachment-0001.html>
More information about the calendarserver-changes
mailing list