[CalendarServer-changes] [5923] CalendarServer/branches/new-store-no-caldavfile
source_changes at macosforge.org
source_changes at macosforge.org
Wed Jul 21 13:29:33 PDT 2010
Revision: 5923
http://trac.macosforge.org/projects/calendarserver/changeset/5923
Author: cdaboo at apple.com
Date: 2010-07-21 13:29:32 -0700 (Wed, 21 Jul 2010)
Log Message:
-----------
Big dump of current state. A lot more removal of CalDAVFile dependencies.
Modified Paths:
--------------
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/test/test_caldav.py
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/export.py
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/util.py
CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/warmup.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/customxml.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/aggregate.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendaruserproxy.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/principal.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/resource.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/test/test_principal.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/dropbox.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/freebusyurl.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/linkresource.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notify.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/utils.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_schedule.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_sharing.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_wrapping.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/util.py
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/timezoneservice.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/__init__.py
CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py
Added Paths:
-----------
CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/test/test_caldav.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/test/test_caldav.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/test/test_caldav.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -47,8 +47,10 @@
from twistedcaldav.stdconfig import DEFAULT_CONFIG
from twistedcaldav.directory.aggregate import AggregateDirectoryService
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
+from twistedcaldav.directory.directory import UnknownRecordTypeError
+from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.sudo import SudoDirectoryService
-from twistedcaldav.directory.directory import UnknownRecordTypeError
from twistedcaldav.test.util import TestCase
from calendarserver.tap.caldav import (
@@ -56,8 +58,6 @@
DelayedStartupProcessMonitor, DelayedStartupLineLogger, TwistdSlaveProcess
)
from calendarserver.provision.root import RootResource
-from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
-from twistedcaldav.static import CalendarHomeProvisioningFile
# Points to top of source tree.
@@ -724,7 +724,7 @@
self.failUnless(isinstance(
root.getChild("calendars"),
- CalendarHomeProvisioningFile
+ DirectoryCalendarHomeProvisioningResource
))
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tap/util.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -36,8 +36,11 @@
from twext.python.log import Logger
from twistedcaldav import memcachepool
+from twistedcaldav.bind import doBind
from twistedcaldav.directory import augment, calendaruserproxy
+from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeProvisioningResource
from twistedcaldav.directory.aggregate import AggregateDirectoryService
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
from twistedcaldav.directory.digest import QopDigestCredentialFactory
from twistedcaldav.directory.internal import InternalDirectoryService
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
@@ -48,8 +51,7 @@
from twistedcaldav.resource import CalDAVResource, AuthenticationWrapper
from twistedcaldav.schedule import IScheduleInboxResource
from twistedcaldav.simpleresource import SimpleResource
-from twistedcaldav.static import CalendarHomeProvisioningFile
-from twistedcaldav.static import AddressBookHomeProvisioningFile, DirectoryBackedAddressBookFile
+from twistedcaldav.static import DirectoryBackedAddressBookFile
from twistedcaldav.timezones import TimezoneCache
from twistedcaldav.timezoneservice import TimezoneServiceResource
from twistedcaldav.util import getMemorySize, getNCPU
@@ -81,18 +83,21 @@
tuples containing: path, resource class, __init__ args list, and optional
authentication scheme ("basic" or "digest").
"""
+
+ # FIXME: this is only here to workaround circular imports
+ doBind()
#
# Default resource classes
#
rootResourceClass = RootResource
principalResourceClass = DirectoryPrincipalProvisioningResource
- calendarResourceClass = CalendarHomeProvisioningFile
+ calendarResourceClass = DirectoryCalendarHomeProvisioningResource
iScheduleResourceClass = IScheduleInboxResource
timezoneServiceResourceClass = TimezoneServiceResource
webCalendarResourceClass = WebCalendarResource
webAdminResourceClass = WebAdminResource
- addressBookResourceClass = AddressBookHomeProvisioningFile
+ addressBookResourceClass = DirectoryAddressBookHomeProvisioningResource
directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookFile
#
@@ -286,16 +291,16 @@
if config.EnableCalDAV:
log.info("Setting up calendar collection: %r" % (calendarResourceClass,))
calendarCollection = calendarResourceClass(
- os.path.join(config.DocumentRoot, "calendars"),
- directory, "/calendars/",
+ directory,
+ "/calendars/",
_newStore,
)
if config.EnableCardDAV:
log.info("Setting up address book collection: %r" % (addressBookResourceClass,))
addressBookCollection = addressBookResourceClass(
- os.path.join(config.DocumentRoot, "addressbooks"),
- directory, "/addressbooks/",
+ directory,
+ "/addressbooks/",
_newStore,
)
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/export.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/export.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/export.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -39,8 +39,9 @@
from twistedcaldav.config import ConfigurationError
from twistedcaldav.ical import Component as iComponent, Property as iProperty
from twistedcaldav.ical import iCalendarProductID
-from twistedcaldav.resource import isCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile, CalendarHomeFile
+from twistedcaldav.resource import isCalendarCollectionResource,\
+ CalendarHomeResource
+from twistedcaldav.static import CalDAVFile
from twistedcaldav.directory.directory import DirectoryService
from calendarserver.tools.util import UsageError
@@ -123,7 +124,7 @@
elif opt in ("-H", "--home"):
path = abspath(arg)
parent = CalDAVFile(dirname(abspath(path)))
- calendarHome = CalendarHomeFile(arg, parent, dummyDirectoryRecord)
+ calendarHome = CalendarHomeResource(arg, parent, dummyDirectoryRecord)
checkExists(calendarHome)
calendarHomes.add(calendarHome)
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/util.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/util.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -22,7 +22,7 @@
"booleanArgument",
]
-import os, sys
+import os
from time import sleep
import socket
from pwd import getpwnam
@@ -38,9 +38,8 @@
from twistedcaldav.config import config, ConfigurationError
from twistedcaldav.directory import augment, calendaruserproxy
from twistedcaldav.directory.aggregate import AggregateDirectoryService
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError
+from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
from twistedcaldav.notify import installNotificationClient
-from twistedcaldav.static import CalendarHomeProvisioningFile
from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
from txdav.common.datastore.file import CommonDataStore
@@ -68,13 +67,13 @@
_newStore = CommonDataStore(FilePath(config.DocumentRoot), True, False)
#
- # Instantiating a CalendarHomeProvisioningResource with a directory
+ # Instantiating a DirectoryCalendarHomeProvisioningResource with a directory
# will register it with the directory (still smells like a hack).
#
# We need that in order to locate calendar homes via the directory.
#
- from twistedcaldav.static import CalendarHomeProvisioningFile
- CalendarHomeProvisioningFile(os.path.join(config.DocumentRoot, "calendars"), self, "/calendars/", _newStore)
+ from twistedcaldav.resource import DirectoryCalendarHomeProvisioningResource
+ DirectoryCalendarHomeProvisioningResource(self, "/calendars/", _newStore)
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
self._principalCollection = DirectoryPrincipalProvisioningResource("/principals/", self)
@@ -143,8 +142,7 @@
# Need a data store
_newStore = CommonDataStore(FilePath(config.DocumentRoot), True, False)
- calendarCollection = CalendarHomeProvisioningFile(
- os.path.join(config.DocumentRoot, "calendars"),
+ calendarCollection = DirectoryCalendarHomeProvisioningResource(
aggregate, "/calendars/",
_newStore,
)
Modified: CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/warmup.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/warmup.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/calendarserver/tools/warmup.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -33,8 +33,9 @@
from os.path import dirname, abspath
from twistedcaldav.config import ConfigurationError
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile, CalendarHomeFile
+from twistedcaldav.resource import isPseudoCalendarCollectionResource,\
+ CalendarHomeResource
+from twistedcaldav.static import CalDAVFile
from twistedcaldav.directory.directory import DirectoryService
from calendarserver.tools.util import loadConfig, getDirectory, dummyDirectoryRecord
@@ -104,7 +105,7 @@
elif opt in ("-H", "--home"):
path = abspath(arg)
parent = CalDAVFile(dirname(abspath(path)))
- calendarHome = CalendarHomeFile(arg, parent, dummyDirectoryRecord)
+ calendarHome = CalendarHomeResource(arg, parent, dummyDirectoryRecord)
checkExists(calendarHome)
calendarHomes.add(calendarHome)
Added: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py (rev 0)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/bind.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -0,0 +1,31 @@
+##
+# Copyright (c) 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.
+##
+
+"""
+Bind methods.
+Have to have this in a separate module for now.
+"""
+
+from twext.web2.dav.util import bindMethods
+
+##
+# Attach methods
+##
+
+def doBind():
+ import twistedcaldav.method
+ from twistedcaldav.resource import CalDAVResource
+ bindMethods(twistedcaldav.method, CalDAVResource)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/customxml.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/customxml.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -325,7 +325,7 @@
class PubSubXMPPURIProperty (davxml.WebDAVTextElement):
"""
- A calendarhomefile property to indicate the pubsub XMPP URI to subscribe to
+ A calendar home property to indicate the pubsub XMPP URI to subscribe to
for notifications.
"""
namespace = calendarserver_namespace
@@ -335,7 +335,7 @@
class PubSubHeartbeatProperty (davxml.WebDAVElement):
"""
- A calendarhomefile property to indicate the pubsub XMPP URI to subscribe to
+ A calendar home property to indicate the pubsub XMPP URI to subscribe to
for server heartbeats.
"""
namespace = calendarserver_namespace
@@ -361,7 +361,7 @@
class PubSubXMPPServerProperty (davxml.WebDAVTextElement):
"""
- A calendarhomefile property to indicate the pubsub XMPP hostname to
+ A calendar home property to indicate the pubsub XMPP hostname to
contact for notifications.
"""
namespace = calendarserver_namespace
@@ -973,6 +973,14 @@
(calendarserver_namespace, "invite-reply") : (0, None),
}
+class Link (davxml.WebDAVEmptyElement):
+ """
+ Denotes a linked resource.
+ """
+ namespace = calendarserver_namespace
+ name = "link"
+
+
##
# Extensions to davxml.ResourceType
##
@@ -988,3 +996,4 @@
davxml.ResourceType.sharedownercalendar = davxml.ResourceType(davxml.Collection(), caldavxml.Calendar(), SharedOwner())
davxml.ResourceType.sharedcalendar = davxml.ResourceType(davxml.Collection(), caldavxml.Calendar(), Shared())
davxml.ResourceType.sharedaddressbook = davxml.ResourceType(davxml.Collection(), carddavxml.AddressBook(), Shared())
+davxml.ResourceType.link = davxml.ResourceType(Link())
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/addressbook.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -25,7 +25,6 @@
"DirectoryAddressBookHomeTypeProvisioningResource",
"DirectoryAddressBookHomeUIDProvisioningResource",
"DirectoryAddressBookHomeResource",
- "GlobalAddressBookResource",
]
from twext.python.log import Logger
@@ -39,22 +38,30 @@
from twistedcaldav.config import config
from twistedcaldav.directory.idirectory import IDirectoryService
-from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn
-from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource
-from twistedcaldav.notifications import NotificationCollectionResource
-from twistedcaldav.resource import CalDAVResource, CalDAVComplianceMixIn
+from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn,\
+ DirectoryReverseProxyResource
+from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource,\
+ DAVResourceWithChildrenMixin
log = Logger()
# Use __underbars__ convention to avoid conflicts with directory resource types.
uidsResourceName = "__uids__"
+# FIXME: copied from resource.py to avoid circular dependency
+class CalDAVComplianceMixIn(object):
+ def davComplianceClasses(self):
+ return (
+ tuple(super(CalDAVComplianceMixIn, self).davComplianceClasses())
+ + config.CalDAVComplianceClasses
+ )
class DirectoryAddressBookProvisioningResource (
AutoProvisioningResourceMixIn,
ReadOnlyResourceMixIn,
CalDAVComplianceMixIn,
DAVResource,
+ DAVResourceWithChildrenMixin,
):
def defaultAccessControlList(self):
return config.ProvisioningResourceACL
@@ -64,7 +71,7 @@
"""
Resource which provisions address book home collections as needed.
"""
- def __init__(self, directory, url):
+ def __init__(self, directory, url, store):
"""
@param directory: an L{IDirectoryService} to provision address books from.
@param url: the canonical URL for the resource.
@@ -73,9 +80,11 @@
assert url.endswith("/"), "Collection URL must end in '/'"
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = IDirectoryService(directory)
self._url = url
+ self._newStore = store
# FIXME: Smells like a hack
directory.addressBookHomesCollection = self
@@ -91,15 +100,15 @@
provisionChild(uidsResourceName)
- def provisionChild(self, recordType):
- raise NotImplementedError("Subclass must implement provisionChild()")
+ def provisionChild(self, name):
+ if name == uidsResourceName:
+ return DirectoryAddressBookHomeUIDProvisioningResource(self)
+ return DirectoryAddressBookHomeTypeProvisioningResource(self, name)
+
def url(self):
return self._url
- def getChild(self, name):
- return self.putChildren.get(name, None)
-
def listChildren(self):
return self.directory.recordTypes()
@@ -142,6 +151,7 @@
assert recordType is not None
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = parent.directory
self.recordType = recordType
@@ -177,7 +187,7 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
- def createSimilarFile(self, path):
+ def makeChild(self, name):
raise HTTPError(responsecode.NOT_FOUND)
##
@@ -199,6 +209,7 @@
class DirectoryAddressBookHomeUIDProvisioningResource (DirectoryAddressBookProvisioningResource):
+
def __init__(self, parent):
"""
@param parent: the parent of this resource
@@ -206,13 +217,30 @@
assert parent is not None
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = parent.directory
self.parent = parent
+
+ # TODO: better way to get this class - perhaps request from the store
+ from twistedcaldav.resource import AddressBookHomeResource
+ self.homeResourceClass = AddressBookHomeResource
def url(self):
return joinURL(self.parent.url(), uidsResourceName)
+ def locateChild(self, request, segments):
+
+ name = segments[0]
+ if name == "":
+ return (self, ())
+
+ record = self.directory.recordWithUID(name)
+ if record:
+ return (self.homeResourceForRecord(record, request), segments[1:])
+ else:
+ return (None, ())
+
def getChild(self, name, record=None):
raise NotImplementedError("DirectoryAddressBookHomeUIDProvisioningResource.getChild no longer exists.")
@@ -220,6 +248,34 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
+ def homeResourceForRecord(self, record, request):
+
+ self.provision()
+ TRANSACTION_KEY = '_newStoreTransaction'
+ transaction = getattr(request, TRANSACTION_KEY, None)
+ if transaction is None:
+ transaction = self.parent._newStore.newTransaction(repr(request))
+ setattr(request, TRANSACTION_KEY, transaction)
+
+ name = record.uid
+
+ if record is None:
+ self.log_msg("No directory record with GUID %r" % (name,))
+ return None
+
+ if not record.enabledForAddressBooks:
+ self.log_msg("Directory record %r is not enabled for address books" % (record,))
+ return None
+
+ assert len(name) > 4, "Directory record has an invalid GUID: %r" % (name,)
+
+ if record.locallyHosted():
+ child = self.homeResourceClass(self, record, transaction)
+ else:
+ child = DirectoryReverseProxyResource(self, record)
+
+ return child
+
##
# DAV
##
@@ -238,7 +294,7 @@
return self.parent.principalForRecord(record)
-class DirectoryAddressBookHomeResource (AutoProvisioningResourceMixIn, CalDAVResource):
+class DirectoryAddressBookHomeResource (AutoProvisioningResourceMixIn, DAVResource, DAVResourceWithChildrenMixin):
"""
Address book home collection resource.
"""
@@ -249,13 +305,15 @@
assert parent is not None
assert record is not None
- CalDAVResource.__init__(self)
+ DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.record = record
self.parent = parent
childlist = ()
if config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled and not config.Sharing.Calendars.Enabled:
+ from twistedcaldav.notifications import NotificationCollectionResource
childlist += (
("notification", NotificationCollectionResource),
)
@@ -275,7 +333,6 @@
childName = "addressbook"
child = self.provisionChild(childName)
- assert isinstance(child, CalDAVResource), "Child %r is not a %s: %r" % (childName, CalDAVResource.__name__, child) #@UndefinedVariable
d = child.createAddressBookCollection()
except:
@@ -370,43 +427,3 @@
is quota-controlled, or C{None} if not quota controlled.
"""
return config.UserQuota if config.UserQuota != 0 else None
-
-class GlobalAddressBookResource (CalDAVResource):
- """
- Global address book. All we care about is making sure permissions are setup.
- """
-
- def resourceType(self, request):
- return succeed(davxml.ResourceType.sharedaddressbook)
-
- def defaultAccessControlList(self):
-
- aces = (
- davxml.ACE(
- davxml.Principal(davxml.Authenticated()),
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- davxml.Privilege(davxml.Write()),
- ),
- davxml.Protected(),
- TwistedACLInheritable(),
- ),
- )
-
- if config.GlobalAddressBook.EnableAnonymousReadAccess:
- aces += (
- davxml.ACE(
- davxml.Principal(davxml.Unauthenticated()),
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- ),
- davxml.Protected(),
- TwistedACLInheritable(),
- ),
- )
- return davxml.ACL(*aces)
-
- def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
- # Permissions here are fixed, and are not subject to inheritance rules, etc.
- return succeed(self.defaultAccessControlList())
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/aggregate.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/aggregate.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -83,7 +83,6 @@
#
# Define calendarHomesCollection as a property so we can set it on contained services
- # See CalendarHomeProvisioningFile.__init__()
#
def _getCalendarHomesCollection(self):
return self._calendarHomesCollection
@@ -97,7 +96,6 @@
#
# Define addressBookHomesCollection as a property so we can set it on contained services
- # See AddressBookHomeProvisioningFile.__init__()
#
def _getAddressBookHomesCollection(self):
return self._addressBookHomesCollection
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendar.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -40,26 +40,32 @@
from twistedcaldav import caldavxml
from twistedcaldav.config import config
from twistedcaldav.dropbox import DropBoxHomeResource
-from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource
-from twistedcaldav.freebusyurl import FreeBusyURLResource
-from twistedcaldav.resource import CalDAVResource, CalDAVComplianceMixIn
-from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
+from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource,\
+ DAVResourceWithChildrenMixin
from twistedcaldav.directory.idirectory import IDirectoryService
from twistedcaldav.directory.wiki import getWikiACL
-from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn
-from twistedcaldav.notifications import NotificationCollectionResource
+from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn,\
+ DirectoryReverseProxyResource
log = Logger()
# Use __underbars__ convention to avoid conflicts with directory resource types.
uidsResourceName = "__uids__"
+# FIXME: copied from resource.py to avoid circular dependency
+class CalDAVComplianceMixIn(object):
+ def davComplianceClasses(self):
+ return (
+ tuple(super(CalDAVComplianceMixIn, self).davComplianceClasses())
+ + config.CalDAVComplianceClasses
+ )
class DirectoryCalendarProvisioningResource (
AutoProvisioningResourceMixIn,
ReadOnlyResourceMixIn,
CalDAVComplianceMixIn,
DAVResource,
+ DAVResourceWithChildrenMixin,
):
def defaultAccessControlList(self):
return config.ProvisioningResourceACL
@@ -69,7 +75,7 @@
"""
Resource which provisions calendar home collections as needed.
"""
- def __init__(self, directory, url):
+ def __init__(self, directory, url, store):
"""
@param directory: an L{IDirectoryService} to provision calendars from.
@param url: the canonical URL for the resource.
@@ -78,9 +84,11 @@
assert url.endswith("/"), "Collection URL must end in '/'"
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = IDirectoryService(directory)
self._url = url
+ self._newStore = store
# FIXME: Smells like a hack
directory.calendarHomesCollection = self
@@ -96,15 +104,15 @@
provisionChild(uidsResourceName)
- def provisionChild(self, recordType):
- raise NotImplementedError("Subclass must implement provisionChild()")
+ def provisionChild(self, name):
+ if name == uidsResourceName:
+ return DirectoryCalendarHomeUIDProvisioningResource(self)
+ return DirectoryCalendarHomeTypeProvisioningResource(self, name)
+
def url(self):
return self._url
- def getChild(self, name):
- return self.putChildren.get(name, None)
-
def listChildren(self):
return self.directory.recordTypes()
@@ -147,6 +155,7 @@
assert recordType is not None
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = parent.directory
self.recordType = recordType
@@ -182,7 +191,7 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
- def createSimilarFile(self, path):
+ def makeChild(self, name):
raise HTTPError(responsecode.NOT_FOUND)
##
@@ -204,6 +213,7 @@
class DirectoryCalendarHomeUIDProvisioningResource (DirectoryCalendarProvisioningResource):
+
def __init__(self, parent):
"""
@param parent: the parent of this resource
@@ -211,13 +221,30 @@
assert parent is not None
DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.directory = parent.directory
self.parent = parent
+
+ # TODO: better way to get this class - perhaps request from the store
+ from twistedcaldav.resource import CalendarHomeResource
+ self.homeResourceClass = CalendarHomeResource
def url(self):
return joinURL(self.parent.url(), uidsResourceName)
+ def locateChild(self, request, segments):
+
+ name = segments[0]
+ if name == "":
+ return (self, ())
+
+ record = self.directory.recordWithUID(name)
+ if record:
+ return (self.homeResourceForRecord(record, request), segments[1:])
+ else:
+ return (None, ())
+
def getChild(self, name, record=None):
raise NotImplementedError("DirectoryCalendarProvisioningResource.getChild no longer exists.")
@@ -225,6 +252,34 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
+ def homeResourceForRecord(self, record, request):
+
+ self.provision()
+ TRANSACTION_KEY = '_newStoreTransaction'
+ transaction = getattr(request, TRANSACTION_KEY, None)
+ if transaction is None:
+ transaction = self.parent._newStore.newTransaction(repr(request))
+ setattr(request, TRANSACTION_KEY, transaction)
+
+ name = record.uid
+
+ if record is None:
+ self.log_msg("No directory record with GUID %r" % (name,))
+ return None
+
+ if not record.enabledForCalendaring:
+ self.log_msg("Directory record %r is not enabled for calendaring" % (record,))
+ return None
+
+ assert len(name) > 4, "Directory record has an invalid GUID: %r" % (name,)
+
+ if record.locallyHosted():
+ child = self.homeResourceClass(self, record, transaction)
+ else:
+ child = DirectoryReverseProxyResource(self, record)
+
+ return child
+
##
# DAV
##
@@ -243,7 +298,7 @@
return self.parent.principalForRecord(record)
-class DirectoryCalendarHomeResource (AutoProvisioningResourceMixIn, CalDAVResource):
+class DirectoryCalendarHomeResource (AutoProvisioningResourceMixIn, DAVResource, DAVResourceWithChildrenMixin):
"""
Calendar home collection resource.
"""
@@ -254,12 +309,14 @@
assert parent is not None
assert record is not None
- CalDAVResource.__init__(self)
+ DAVResource.__init__(self)
+ DAVResourceWithChildrenMixin.__init__(self)
self.record = record
self.parent = parent
# Cache children which must be of a specific type
+ from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
childlist = (
("inbox" , ScheduleInboxResource ),
("outbox", ScheduleOutboxResource),
@@ -269,10 +326,12 @@
("dropbox", DropBoxHomeResource),
)
if config.FreeBusyURL.Enabled:
+ from twistedcaldav.freebusyurl import FreeBusyURLResource
childlist += (
("freebusy", FreeBusyURLResource),
)
if config.Sharing.Enabled and config.Sharing.Calendars.Enabled:
+ from twistedcaldav.notifications import NotificationCollectionResource
childlist += (
("notification", NotificationCollectionResource),
)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendaruserproxy.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/calendaruserproxy.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -128,13 +128,13 @@
"""
return ProxyDBService
- def resourceType(self, request):
+ def resourceType(self):
if self.proxyType == "calendar-proxy-read":
- return succeed(davxml.ResourceType.calendarproxyread)
+ return davxml.ResourceType.calendarproxyread
elif self.proxyType == "calendar-proxy-write":
- return succeed(davxml.ResourceType.calendarproxywrite)
+ return davxml.ResourceType.calendarproxywrite
else:
- return super(CalendarUserProxyPrincipalResource, self).resourceType(request)
+ return super(CalendarUserProxyPrincipalResource, self).resourceType()
def isProxyType(self, read_write):
if (
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/principal.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/principal.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -936,7 +936,6 @@
def calendarHome(self, request):
# FIXME: self.record.service.calendarHomesCollection smells like a hack
- # See CalendarHomeProvisioningFile.__init__()
service = self.record.service
if hasattr(service, "calendarHomesCollection"):
return service.calendarHomesCollection.homeForDirectoryRecord(self.record, request)
@@ -960,7 +959,6 @@
def addressBookHome(self, request):
# FIXME: self.record.service.addressBookHomesCollection smells like a hack
- # See AddressBookHomeProvisioningFile.__init__()
service = self.record.service
if hasattr(service, "addressBookHomesCollection"):
return service.addressBookHomesCollection.homeForDirectoryRecord(self.record, request)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/resource.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/resource.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.client.reverseproxy import ReverseProxyResource
+from twext.web2.dav.util import joinURL
"""
Implements a directory-backed principal hierarchy.
@@ -69,3 +71,16 @@
result = (yield super(AutoProvisioningResourceMixIn, self).locateChild(request, segments))
returnValue(result)
+
+class DirectoryReverseProxyResource(ReverseProxyResource):
+
+ def __init__(self, parent, record):
+ self.parent = parent
+ self.record = record
+
+ super(DirectoryReverseProxyResource, self).__init__(self.record.hostedAt)
+
+ def url(self):
+ return joinURL(self.parent.url(), self.record.uid)
+
+
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/test/test_principal.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directory/test/test_principal.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -23,7 +23,6 @@
from twext.web2.dav.resource import AccessDeniedError
from twext.web2.test.test_server import SimpleRequest
-from twistedcaldav.static import CalendarHomeProvisioningFile
from twistedcaldav.config import config
from twistedcaldav.directory import augment, calendaruserproxy
from twistedcaldav.directory.directory import DirectoryService
@@ -358,21 +357,19 @@
# Need to create a calendar home provisioner for each service.
calendarRootResources = {}
- # Need a data store
- _newStore = CommonDataStore(FilePath(self.docroot), True, False)
-
for directory in self.directoryServices:
- url = "/homes_" + directory.__class__.__name__ + "/"
- path = os.path.join(self.docroot, url[1:])
+ path = os.path.join(self.docroot, directory.__class__.__name__)
if os.path.exists(path):
rmdir(path)
os.mkdir(path)
- provisioningResource = CalendarHomeProvisioningFile(
- path,
+ # Need a data store
+ _newStore = CommonDataStore(path, True, False)
+
+ provisioningResource = DirectoryCalendarHomeProvisioningResource(
directory,
- url,
+ "/calendars/",
_newStore
)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/directorybackedaddressbook.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -94,8 +94,8 @@
),
)
- def resourceType(self, request):
- return succeed(davxml.ResourceType.addressbook)
+ def resourceType(self):
+ return davxml.ResourceType.addressbook
def isDirectoryBackedAddressBookCollection(self):
return True
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/dropbox.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/dropbox.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/dropbox.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -41,8 +41,8 @@
"""
Drop box collection resource.
"""
- def resourceType(self, request):
- return succeed(davxml.ResourceType.dropboxhome)
+ def resourceType(self):
+ return davxml.ResourceType.dropboxhome
def isCollection(self):
return True
@@ -83,8 +83,8 @@
"""
Drop box resource.
"""
- def resourceType(self, request):
- return succeed(davxml.ResourceType.dropbox)
+ def resourceType(self):
+ return davxml.ResourceType.dropbox
def isCollection(self):
return True
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/extensions.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -21,6 +21,7 @@
__all__ = [
"DAVResource",
+ "DAVResourceWithChildren",
"DAVPrincipalResource",
"DAVFile",
"ReadOnlyWritePropertiesResourceMixIn",
@@ -515,7 +516,7 @@
for name in sorted(self.listChildren()):
child = self.getChild(name)
- url, name, size, lastModified, contentType = (yield self.getChildDirectoryEntry(child, name, request))
+ url, name, size, lastModified, contentType = self.getChildDirectoryEntry(child, name, request)
# FIXME: gray out resources that are not readable
output.append(
@@ -624,7 +625,6 @@
result = (yield gotProperties(qnames))
returnValue(result)
- @inlineCallbacks
def getChildDirectoryEntry(self, child, name, request):
def orNone(value, default="?", f=None):
if value is None:
@@ -643,7 +643,7 @@
size = child.contentLength()
lastModified = child.lastModified()
rtypes = []
- fullrtype = (yield child.resourceType(request))
+ fullrtype = child.resourceType()
for rtype in fullrtype.children:
rtypes.append(rtype.name)
if rtypes:
@@ -663,8 +663,16 @@
size = None
lastModified = None
contentType = None
+ if hasattr(child, "resourceType"):
+ rtypes = []
+ fullrtype = child.resourceType()
+ for rtype in fullrtype.children:
+ rtypes.append(rtype.name)
+ if rtypes:
+ contentType = "(%s)" % (", ".join(rtypes),)
+
- returnValue((
+ return ((
url,
name,
orNone(size),
@@ -698,14 +706,59 @@
return super(DAVResource, self).render(request)
- def resourceType(self, request):
+ def resourceType(self):
# Allow live property to be overridden by dead property
if self.deadProperties().contains((dav_namespace, "resourcetype")):
- return succeed(self.deadProperties().get((dav_namespace, "resourcetype")))
- return succeed(davxml.ResourceType())
+ return self.deadProperties().get((dav_namespace, "resourcetype"))
+ return davxml.ResourceType()
+class DAVResourceWithChildrenMixin (object):
+ """
+ Bits needed from twext.web2.static
+ """
+ def __init__(self):
+ self.putChildren = {}
+
+ def putChild(self, name, child):
+ """
+ Register a child with the given name with this resource.
+ @param name: the name of the child (a URI path segment)
+ @param child: the child to register
+ """
+ self.putChildren[name] = child
+
+ def getChild(self, name):
+ """
+ Look up a child resource.
+ @return: the child of this resource with the given name.
+ """
+ if name == "":
+ return self
+
+ result = self.putChildren.get(name, None)
+ if not result:
+ result = self.makeChild(name)
+ return result
+
+ def makeChild(self):
+ # Subclasses with real children need to override this and return the appropriate object
+ return None
+
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ return self.putChildren.keys()
+
+ def locateChild(self, req, segments):
+ """
+ See L{IResource}C{.locateChild}.
+ """
+ # If getChild() finds a child resource, return it
+ return (self.getChild(segments[0]), segments[1:])
+
class DAVPrincipalResource (DirectoryPrincipalPropertySearchMixIn,
SuperDAVPrincipalResource, LoggingMixIn,
DirectoryRenderingMixIn):
@@ -733,7 +786,7 @@
if namespace == dav_namespace:
if name == "resourcetype":
- rtype = (yield self.resourceType(request))
+ rtype = self.resourceType()
returnValue(rtype)
elif namespace == calendarserver_namespace:
@@ -775,14 +828,14 @@
def expandedGroupMemberships(self):
return succeed(())
- def resourceType(self, request):
+ def resourceType(self):
# Allow live property to be overridden by dead property
if self.deadProperties().contains((dav_namespace, "resourcetype")):
- return succeed(self.deadProperties().get((dav_namespace, "resourcetype")))
+ return self.deadProperties().get((dav_namespace, "resourcetype"))
if self.isCollection():
- return succeed(davxml.ResourceType(davxml.Principal(), davxml.Collection()))
+ return davxml.ResourceType(davxml.Principal(), davxml.Collection())
else:
- return succeed(davxml.ResourceType(davxml.Principal()))
+ return davxml.ResourceType(davxml.Principal())
@@ -791,24 +844,14 @@
"""
Extended L{twext.web2.dav.static.DAVFile} implementation.
"""
- def readProperty(self, property, request):
- if type(property) is tuple:
- qname = property
- else:
- qname = property.qname()
- if qname == (dav_namespace, "resourcetype"):
- return self.resourceType(request)
-
- return super(DAVFile, self).readProperty(property, request)
-
- def resourceType(self, request):
+ def resourceType(self):
# Allow live property to be overridden by dead property
if self.deadProperties().contains((dav_namespace, "resourcetype")):
- return succeed(self.deadProperties().get((dav_namespace, "resourcetype")))
+ return self.deadProperties().get((dav_namespace, "resourcetype"))
if self.isCollection():
- return succeed(davxml.ResourceType.collection)
- return succeed(davxml.ResourceType.empty)
+ return davxml.ResourceType.collection
+ return davxml.ResourceType.empty
def render(self, request):
if not self.fp.exists():
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/freebusyurl.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/freebusyurl.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/freebusyurl.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -32,7 +32,6 @@
from twext.web2 import responsecode
from twext.web2.dav import davxml
from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.method.propfind import http_PROPFIND
from twext.web2.dav.util import joinURL
from twext.web2.http import HTTPError
from twext.web2.http import Response
@@ -48,7 +47,7 @@
from twistedcaldav.ical import parse_datetime
from twistedcaldav.ical import parse_duration
from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn
-from twistedcaldav.resource import deliverSchedulePrivilegeSet
+from twistedcaldav.schedule import deliverSchedulePrivilegeSet
from twistedcaldav.scheduling.caldav import ScheduleViaCalDAV
from twistedcaldav.scheduling.cuaddress import LocalCalendarUser
from twistedcaldav.scheduling.scheduler import Scheduler
@@ -105,8 +104,8 @@
)
return davxml.ACL(*aces)
- def resourceType(self, request):
- return succeed(davxml.ResourceType.freebusyurl)
+ def resourceType(self):
+ return davxml.ResourceType.freebusyurl
def contentType(self):
return MimeType("text", "calendar", charset="utf-8")
@@ -146,8 +145,6 @@
"""
return self._processFBURL(request)
- http_PROPFIND = http_PROPFIND
-
@inlineCallbacks
def _processFBURL(self, request):
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/linkresource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/linkresource.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/linkresource.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -18,15 +18,24 @@
from twisted.internet.defer import inlineCallbacks, returnValue, maybeDeferred
-from twistedcaldav.resource import CalDAVComplianceMixIn
from twext.web2.http import HTTPError
from twext.web2 import responsecode
from twext.web2.resource import WrapperResource
+from twistedcaldav.config import config
+from twext.web2.dav import davxml
__all__ = [
"LinkResource",
]
+# FIXME: copied from resource.py to avoid circular dependency
+class CalDAVComplianceMixIn(object):
+ def davComplianceClasses(self):
+ return (
+ tuple(super(CalDAVComplianceMixIn, self).davComplianceClasses())
+ + config.CalDAVComplianceClasses
+ )
+
"""
A resource that is a soft-link to another.
"""
@@ -55,11 +64,8 @@
def isCollection(self):
return True
- @inlineCallbacks
- def resourceType(self, request):
- hosted = (yield self.linkedResource(request))
- result = (yield hosted.resourceType(request))
- returnValue(result)
+ def resourceType(self):
+ return self._linkedResource.resourceType() if hasattr(self, "_linkedResource") else davxml.ResourceType.link
def locateChild(self, request, segments):
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/mail.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -61,11 +61,12 @@
from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.ical import Property
from twistedcaldav.localization import translationTo
+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, deliverSchedulePrivilegeSet
+from twistedcaldav.static import CalDAVFile
from twistedcaldav.util import AuthorizedHTTPGetter
from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
@@ -219,8 +220,8 @@
return succeed(self.iMIPACL)
- def resourceType(self, request):
- return succeed(davxml.ResourceType.ischeduleinbox)
+ def resourceType(self):
+ return davxml.ResourceType.ischeduleinbox
def isCollection(self):
return False
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/acl.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -29,8 +29,8 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.resource import isAddressBookCollectionResource,\
- isPseudoCalendarCollectionResource
-from twistedcaldav.static import AddressBookHomeFile, CalendarHomeFile
+ isPseudoCalendarCollectionResource,\
+ CalendarHomeResource, AddressBookHomeResource
log = Logger()
@@ -42,7 +42,7 @@
#
if self.exists():
- if isinstance(self, CalendarHomeFile) or isinstance(self, AddressBookHomeFile):
+ if isinstance(self, CalendarHomeResource) or isinstance(self, AddressBookHomeResource):
raise HTTPError(responsecode.NOT_ALLOWED)
parentURL = parentForURL(request.uri)
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-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/method/put_common.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -674,7 +674,7 @@
if do_implicit_action and self.allowImplicitSchedule:
# Cannot do implicit in sharee's shared calendar
- isvirt = (yield self.destinationparent.isVirtualShare(self.request))
+ isvirt = self.destinationparent.isVirtualShare()
if isvirt:
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notifications.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -29,7 +29,6 @@
from twisted.internet.defer import succeed, inlineCallbacks, returnValue
-from twistedcaldav.method.propfind import http_PROPFIND
from twistedcaldav.resource import ReadOnlyNoCopyResourceMixIn, CalDAVResource
from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
@@ -65,13 +64,9 @@
if response == responsecode.NO_CONTENT:
yield self._parent.removedNotifictionMessage(request, self.resourceName())
returnValue(response)
-
- http_PROPFIND = http_PROPFIND
class NotificationCollectionResource(ReadOnlyNoCopyResourceMixIn, CalDAVResource):
- http_PROPFIND = http_PROPFIND
-
def notificationsDB(self):
if not hasattr(self, "_notificationsDB"):
@@ -81,8 +76,8 @@
def isCollection(self):
return True
- def resourceType(self, request):
- return succeed(davxml.ResourceType.notification)
+ def resourceType(self):
+ return davxml.ResourceType.notification
@inlineCallbacks
def addNotification(self, request, uid, xmltype, xmldata):
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notify.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notify.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/notify.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -606,7 +606,7 @@
Uses pubsub XMPP requests to let subscribers know when there
has been a change made to a DAV resource (currently just
- CalendarHomeFiles). Uses XMPP login info from the config file
+ CalendarHomeResources). Uses XMPP login info from the config file
to determine which pubsub service to connect to. When it's
time to send a notification, XMPPNotifier computes a node path
corresponding to the DAV resource and emits a publish request
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/resource.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -14,6 +14,14 @@
# 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.
@@ -40,7 +48,7 @@
from twext.web2.dav.http import ErrorResponse
from twisted.internet import reactor
-from twisted.internet.defer import Deferred, succeed, maybeDeferred
+from twisted.internet.defer import Deferred, succeed, maybeDeferred, fail
from twisted.internet.defer import inlineCallbacks, returnValue
from twext.web2 import responsecode
from twext.web2.dav import davxml
@@ -50,8 +58,7 @@
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,\
- bindMethods
+from twext.web2.dav.util import joinURL, parentForURL, unimplemented, normalizeURL
from twext.web2.http import HTTPError, RedirectResponse, StatusResponse, Response
from twext.web2.http_headers import MimeType
from twext.web2.stream import MemoryStream
@@ -64,15 +71,17 @@
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeResource
from twistedcaldav.extensions import DAVResource, DAVPrincipalResource,\
PropertyNotFoundError
from twistedcaldav.ical import Component
from twistedcaldav.ical import Component as iComponent
from twistedcaldav.ical import allowedComponents
from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
-from twistedcaldav.notify import getPubSubConfiguration, getPubSubPath
+from twistedcaldav.notify import getPubSubConfiguration, getPubSubPath,\
+ ClientNotifier, getPubSubXMPPURI, getPubSubHeartbeatURI
from twistedcaldav.notify import getNodeCacher, NodeCreationException
-from twistedcaldav.sharing import SharedCollectionMixin
+from twistedcaldav.sharing import SharedCollectionMixin, SharedHomeMixin
from twistedcaldav.vcard import Component as vComponent
from txdav.common.icommondatastore import InternalDataStoreError
@@ -132,43 +141,6 @@
def http_COPY(self, request): return responsecode.FORBIDDEN
-def _schedulePrivilegeSet(deliver):
- 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 = list(supported_privilege.childrenOfType(davxml.SupportedPrivilege))
- all_supported_privileges.append(
- davxml.SupportedPrivilege(
- davxml.Privilege(caldavxml.ScheduleDeliver() if deliver else caldavxml.ScheduleSend()),
- davxml.Description("schedule privileges for current principal", **{"xml:lang": "en"}),
- ),
- )
- if config.Scheduling.CalDAV.OldDraftCompatibility:
- all_supported_privileges.append(
- davxml.SupportedPrivilege(
- davxml.Privilege(caldavxml.Schedule()),
- davxml.Description("old-style schedule privileges for current principal", **{"xml:lang": "en"}),
- ),
- )
- top_supported_privileges.append(
- davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
- )
- edited = True
- 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 schedulePrivilegeSet"
-
- return davxml.SupportedPrivilegeSet(*top_supported_privileges)
-
-deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
-sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
-
def _calendarPrivilegeSet ():
edited = False
@@ -464,7 +436,7 @@
else:
qname = property.qname()
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if isvirt:
if self.isShadowableProperty(qname):
ownerPrincipal = (yield self.resourceOwnerPrincipal(request))
@@ -503,7 +475,7 @@
else:
qname = property.qname()
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if self.isCalendarCollection() or self.isAddressBookCollection():
# Push notification DAV property "pushkey"
@@ -550,6 +522,9 @@
owner = (yield self.owner(request))
returnValue(davxml.Owner(owner))
+ elif qname == davxml.ResourceType.qname():
+ returnValue(self.resourceType())
+
elif qname == davxml.ResourceID.qname():
returnValue(davxml.ResourceID(davxml.HRef.fromString(self.resourceID())))
@@ -628,7 +603,7 @@
returnValue(customxml.AllowedSharingModes(customxml.CanBeShared()))
elif qname == customxml.SharedURL.qname():
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if isvirt:
returnValue(customxml.SharedURL(davxml.HRef.fromString(self._share.hosturl)))
@@ -645,7 +620,7 @@
)
# Per-user Dav props currently only apply to a sharee's copy of a calendar
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if isvirt and (self.isShadowableProperty(property.qname()) or (not self.isGlobalProperty(property.qname()))):
yield self._preProcessWriteProperty(property, request)
ownerPrincipal = (yield self.resourceOwnerPrincipal(request))
@@ -746,14 +721,14 @@
sawShare = [child for child in property.children if child.qname() == (calendarserver_namespace, "shared-owner")]
if not shared and sawShare:
# Owner is trying to share a collection
- yield self.upgradeToShare(request)
+ self.upgradeToShare()
elif shared and not sawShare:
# Remove share
yield self.downgradeFromShare(request)
returnValue(None)
else:
# resourcetype cannot be changed but we will allow it to be set to the same value
- currentType = (yield self.resourceType(request))
+ currentType = self.resourceType()
if currentType == property:
returnValue(None)
@@ -769,7 +744,7 @@
def accessControlList(self, request, *args, **kwargs):
acls = None
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if isvirt:
acls = self.shareeAccessControlList()
@@ -826,7 +801,7 @@
Return the DAV:owner property value (MUST be a DAV:href or None).
"""
- isVirt = (yield self.isVirtualShare(request))
+ isVirt = self.isVirtualShare()
if isVirt:
parent = (yield self.locateParent(request, self._share.hosturl))
else:
@@ -842,7 +817,7 @@
"""
Return the DAV:owner property value (MUST be a DAV:href or None).
"""
- isVirt = (yield self.isVirtualShare(request))
+ isVirt = self.isVirtualShare()
if isVirt:
parent = (yield self.locateParent(request, self._share.hosturl))
else:
@@ -860,7 +835,7 @@
collection it will be the sharee, otherwise it will be the regular the ownerPrincipal.
"""
- isVirt = (yield self.isVirtualShare(request))
+ isVirt = self.isVirtualShare()
if isVirt:
returnValue(self._shareePrincipal)
else:
@@ -1013,7 +988,7 @@
if depth != "0" and self.isCollection():
basepath = request.urlForResource(self)
- children = self.listChildren()
+ children = list(self.listChildren())
getChild()
else:
completionDeferred.callback(None)
@@ -1339,14 +1314,14 @@
"""
sharedParent = None
- isvirt = (yield self.isVirtualShare(request))
+ isvirt = self.isVirtualShare()
if isvirt:
# A virtual share's quota root is the resource owner's root
sharedParent = (yield request.locateResource(parentForURL(self._share.hosturl)))
else:
parent = (yield self.locateParent(request, request.urlForResource(self)))
if isCalendarCollectionResource(parent) or isAddressBookCollectionResource(parent):
- isvirt = (yield parent.isVirtualShare(request))
+ isvirt = parent.isVirtualShare()
if isvirt:
# A virtual share's quota root is the resource owner's root
sharedParent = (yield request.locateResource(parentForURL(parent._share.hosturl)))
@@ -1358,6 +1333,109 @@
returnValue(result)
+ # Collection sync stuff
+
+ def whatchanged(self, client_token):
+
+ current_token = str(self.readDeadProperty(customxml.GETCTag))
+ current_uuid, current_revision = current_token.split("#", 1)
+ current_revision = int(current_revision)
+
+ if client_token:
+ try:
+ caluuid, revision = client_token.split("#", 1)
+ revision = int(revision)
+
+ # Check client token validity
+ if caluuid != current_uuid:
+ raise ValueError
+ if revision > current_revision:
+ raise ValueError
+ except ValueError:
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
+ else:
+ revision = 0
+
+ try:
+ changed, removed = self.index().whatchanged(revision)
+ except SyncTokenValidException:
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
+
+ return changed, removed, current_token
+
+ @inlineCallbacks
+ def bumpSyncToken(self):
+ """
+ Increment the sync-token which is also the ctag.
+
+ return: a deferred that returns the new revision number
+ """
+ assert self.isCollection()
+
+ # Need to lock
+ lock = MemcacheLock("ResourceLock", self.fp.path, timeout=60.0)
+ try:
+ try:
+ yield lock.acquire()
+ except MemcacheLockTimeoutError:
+ raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use on the server." % (self.uri,)))
+
+ try:
+ token = str(self.readDeadProperty(customxml.GETCTag))
+ caluuid, revision = token.split("#", 1)
+ revision = int(revision) + 1
+ token = "%s#%d" % (caluuid, revision,)
+
+ except (HTTPError, ValueError):
+ # Initialise it
+ caluuid = uuid4()
+ revision = 1
+ token = "%s#%d" % (caluuid, revision,)
+
+ yield self.updateCTag(token)
+ returnValue(revision)
+ finally:
+ yield lock.clean()
+
+ def initSyncToken(self):
+ """
+ Create a new sync-token which is also the ctag.
+ """
+ # FIXME: new implementation is in txcaldav.file, this should be
+ # deleted.
+ assert self.isCollection()
+ # Initialise it
+ caluuid = uuid4()
+ revision = 1
+ token = "%s#%d" % (caluuid, revision,)
+ try:
+ self.writeDeadProperty(customxml.GETCTag(token))
+ except:
+ return fail(Failure())
+
+ def getSyncToken(self):
+ """
+ Return current sync-token value.
+ """
+ assert self.isCollection()
+
+ return str(self.readDeadProperty(customxml.GETCTag))
+
+ def updateCTag(self, token=None):
+ assert self.isCollection()
+
+ if not token:
+ token = str(datetime.datetime.now())
+ try:
+ self.writeDeadProperty(customxml.GETCTag(token))
+ except:
+ return fail(Failure())
+
+ if hasattr(self, 'clientNotifier'):
+ self.clientNotifier.notify(op="update")
+
+ return succeed(True)
+
class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
"""
CalDAV principal collection.
@@ -1636,7 +1714,570 @@
"""
return None
+class CalendarHomeResource(SharedHomeMixin, DirectoryCalendarHomeResource, CalDAVResource):
+ """
+ Calendar home collection resource.
+ """
+ def __init__(self, parent, record, transaction):
+ """
+ """
+ self.associateWithTransaction(transaction)
+
+ # TODO: when calendar home gets a resourceID( ) method, remove
+ # the "id=record.uid" keyword from this call:
+ self.clientNotifier = ClientNotifier(self, id=record.uid)
+ storeHome = transaction.calendarHomeWithUID(record.uid)
+ if storeHome is not None:
+ created = False
+ else:
+ storeHome = transaction.calendarHomeWithUID(
+ record.uid, create=True
+ )
+ created = True
+ self._newStoreCalendarHome = storeHome
+ CalDAVResource.__init__(self)
+ DirectoryCalendarHomeResource.__init__(self, parent, record)
+ from twistedcaldav.storebridge import _NewStorePropertiesWrapper
+ self._dead_properties = _NewStorePropertiesWrapper(
+ self._newStoreCalendarHome.properties()
+ )
+ if created:
+ # This is a bit of a hack. Really we ought to be always generating
+ # this URL live from a back-end method that tells us what the
+ # default calendar is.
+ inbox = self.getChild("inbox")
+ childURL = joinURL(self.url(), "calendar")
+ inbox.processFreeBusyCalendar(childURL, True)
+
+
+ def liveProperties(self):
+
+ return super(CalendarHomeResource, self).liveProperties() + (
+ (customxml.calendarserver_namespace, "push-transports"),
+ (customxml.calendarserver_namespace, "pushkey"),
+ (customxml.calendarserver_namespace, "xmpp-uri"),
+ (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"),
+ (customxml.calendarserver_namespace, "xmpp-server"),
+ )
+
+ def sharesDB(self):
+ """
+ Retrieve the new-style shares DB wrapper.
+ """
+ if not hasattr(self, "_sharesDB"):
+ self._sharesDB = self._newStoreCalendarHome.retrieveOldShares()
+ return self._sharesDB
+
+
+ def exists(self):
+ # FIXME: tests
+ return True
+
+
+ def quotaSize(self, request):
+ # FIXME: tests, workingness
+ return succeed(0)
+
+
+ def provision(self):
+ if config.Sharing.Enabled and config.Sharing.Calendars.Enabled and self.exists():
+ self.provisionShares()
+ return
+
+ def provisionChild(self, name):
+ from twistedcaldav.storebridge import StoreScheduleInboxResource
+ from twistedcaldav.storebridge import DropboxCollection
+ if config.EnableDropBox:
+ DropBoxHomeFileClass = DropboxCollection
+ else:
+ DropBoxHomeFileClass = None
+
+ if config.FreeBusyURL.Enabled:
+ from twistedcaldav.freebusyurl import FreeBusyURLResource
+ FreeBusyURLResourceClass = FreeBusyURLResource
+ else:
+ FreeBusyURLResourceClass = None
+
+ # For storebridge stuff we special case this
+ if name == "notification" and config.Sharing.Enabled and config.Sharing.Calendars.Enabled:
+ return self.createNotificationsCollection()
+
+ from twistedcaldav.schedule import ScheduleOutboxResource
+ cls = {
+ "inbox" : StoreScheduleInboxResource,
+ "outbox" : ScheduleOutboxResource,
+ "dropbox" : DropBoxHomeFileClass,
+ "freebusy" : FreeBusyURLResourceClass,
+ }.get(name, None)
+
+ if cls is not None:
+ child = cls(self)
+ child.clientNotifier = self.clientNotifier.clone(child,
+ label="collection")
+ return child
+ return self.makeChild(name)
+
+ def createNotificationsCollection(self):
+
+ txn = self._newStoreCalendarHome._transaction
+ notifications = txn.notificationsWithUID(self._newStoreCalendarHome.uid())
+
+ from twistedcaldav.storebridge import StoreNotificationCollectionResource
+ similar = StoreNotificationCollectionResource(
+ notifications,
+ self._newStoreCalendarHome,
+ principalCollections = self.principalCollections(),
+ )
+ self.propagateTransaction(similar)
+ similar.clientNotifier = self.clientNotifier.clone(similar,
+ label="collection")
+ return similar
+
+ def makeChild(self, name):
+
+ newCalendar = self._newStoreCalendarHome.calendarWithName(name)
+ if newCalendar is None:
+ # Local imports.due to circular dependency between modules.
+ from twistedcaldav.storebridge import (
+ ProtoCalendarCollectionResource)
+ similar = ProtoCalendarCollectionResource(
+ self._newStoreCalendarHome,
+ principalCollections=self.principalCollections()
+ )
+ else:
+ from twistedcaldav.storebridge import CalendarCollectionResource
+ similar = CalendarCollectionResource(
+ newCalendar, self._newStoreCalendarHome,
+ principalCollections=self.principalCollections()
+ )
+ self.propagateTransaction(similar)
+ similar.clientNotifier = self.clientNotifier.clone(similar,
+ label="collection")
+ return similar
+
+ def getChild(self, name):
+ # This avoids finding case variants of put children on case-insensitive filesystems.
+ if name not in self.putChildren and name.lower() in (x.lower() for x in self.putChildren):
+ return None
+
+ return super(CalendarHomeResource, self).getChild(name)
+
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ children = set(self.putChildren.keys())
+ children.update(self._newStoreCalendarHome.listCalendars())
+ return children
+
+ def readProperty(self, property, request):
+ if type(property) is tuple:
+ qname = property
+ else:
+ qname = property.qname()
+
+ if qname == (customxml.calendarserver_namespace, "push-transports"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if (pubSubConfiguration['enabled'] and
+ getattr(self, "clientNotifier", None) is not None):
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ children = []
+ if pubSubConfiguration['aps-bundle-id']:
+ children.append(
+ customxml.PubSubTransportProperty(
+ customxml.PubSubSubscriptionProperty(
+ davxml.HRef(
+ pubSubConfiguration['subscription-url']
+ ),
+ ),
+ customxml.PubSubAPSBundleIDProperty(
+ pubSubConfiguration['aps-bundle-id']
+ ),
+ type="APSD",
+ )
+ )
+ if pubSubConfiguration['xmpp-server']:
+ children.append(
+ customxml.PubSubTransportProperty(
+ customxml.PubSubXMPPServerProperty(
+ pubSubConfiguration['xmpp-server']
+ ),
+ customxml.PubSubXMPPURIProperty(
+ getPubSubXMPPURI(id, pubSubConfiguration)
+ ),
+ type="XMPP",
+ )
+ )
+
+ propVal = customxml.PubSubPushTransportsProperty(*children)
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the value
+ d.addBoth(lambda ignored: propVal)
+ return d
+
+
+ else:
+ return succeed(customxml.PubSubPushTransportsProperty())
+
+ if qname == (customxml.calendarserver_namespace, "pushkey"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ if getattr(self, "clientNotifier", None) is not None:
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ propVal = customxml.PubSubXMPPPushKeyProperty(nodeName)
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the xmpp-uri value
+ d.addBoth(lambda ignored: propVal)
+ return d
+ else:
+ return succeed(customxml.PubSubXMPPPushKeyProperty())
+
+
+ if qname == (customxml.calendarserver_namespace, "xmpp-uri"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ if getattr(self, "clientNotifier", None) is not None:
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ propVal = customxml.PubSubXMPPURIProperty(
+ getPubSubXMPPURI(id, pubSubConfiguration))
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the xmpp-uri value
+ d.addBoth(lambda ignored: propVal)
+ return d
+ else:
+ return succeed(customxml.PubSubXMPPURIProperty())
+
+ elif qname == (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ return succeed(
+ customxml.PubSubHeartbeatProperty(
+ customxml.PubSubHeartbeatURIProperty(
+ getPubSubHeartbeatURI(pubSubConfiguration)
+ ),
+ customxml.PubSubHeartbeatMinutesProperty(
+ str(pubSubConfiguration['heartrate'])
+ )
+ )
+ )
+ else:
+ return succeed(customxml.PubSubHeartbeatURIProperty())
+
+ elif qname == (customxml.calendarserver_namespace, "xmpp-server"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ return succeed(customxml.PubSubXMPPServerProperty(
+ pubSubConfiguration['xmpp-server']))
+ else:
+ return succeed(customxml.PubSubXMPPServerProperty())
+
+ return super(CalendarHomeResource, self).readProperty(property, request)
+
+ http_ACL = None # ACL method not supported
+
+class AddressBookHomeResource (LinkFollowerMixIn, SharedHomeMixin, DirectoryAddressBookHomeResource, CalDAVResource):
+ """
+ Address book home collection resource.
+ """
+
+ def __init__(self, parent, record, transaction):
+ """
+ """
+
+ self.associateWithTransaction(transaction)
+
+ # TODO: when addressbook home gets a resourceID( ) method, remove
+ # the "id=record.uid" keyword from this call:
+ self.clientNotifier = ClientNotifier(self, id=record.uid)
+ self._newStoreAddressBookHome = (
+ transaction.addressbookHomeWithUID(record.uid, create=True)
+ )
+ CalDAVResource.__init__(self)
+ DirectoryAddressBookHomeResource.__init__(self, parent, record)
+
+ from twistedcaldav.storebridge import _NewStorePropertiesWrapper
+ self._dead_properties = _NewStorePropertiesWrapper(
+ self._newStoreAddressBookHome.properties()
+ )
+
+
+ def liveProperties(self):
+ return super(AddressBookHomeResource, self).liveProperties() + (
+ (customxml.calendarserver_namespace, "push-transports"),
+ (customxml.calendarserver_namespace, "pushkey"),
+ (customxml.calendarserver_namespace, "xmpp-uri"),
+ (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"),
+ (customxml.calendarserver_namespace, "xmpp-server"),
+ )
+
+ def sharesDB(self):
+ """
+ Retrieve the new-style shares DB wrapper.
+ """
+ if not hasattr(self, "_sharesDB"):
+ self._sharesDB = self._newStoreAddressBookHome.retrieveOldShares()
+ return self._sharesDB
+
+
+ def exists(self):
+ # FIXME: tests
+ return True
+
+
+ def quotaSize(self, request):
+ # FIXME: tests, workingness
+ return succeed(0)
+
+
+ def provision(self):
+ if config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled:
+ self.provisionShares()
+ self.provisionLinks()
+
+ def provisionLinks(self):
+
+ if not hasattr(self, "_provisionedLinks"):
+ if config.GlobalAddressBook.Enabled:
+ self.putChild(
+ config.GlobalAddressBook.Name,
+ LinkResource(self, "/addressbooks/public/global/addressbook/"),
+ )
+ self._provisionedLinks = True
+
+ def provisionChild(self, name):
+
+ # For storebridge stuff we special case this
+ 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)
+
+ def createNotificationsCollection(self):
+
+ txn = self._newStoreAddressBookHome._transaction
+ notifications = txn.notificationsWithUID(self._newStoreAddressBookHome.uid())
+
+ from twistedcaldav.storebridge import StoreNotificationCollectionResource
+ similar = StoreNotificationCollectionResource(
+ notifications,
+ self._newStoreAddressBookHome,
+ principalCollections = self.principalCollections(),
+ )
+ self.propagateTransaction(similar)
+ similar.clientNotifier = self.clientNotifier.clone(similar,
+ label="collection")
+ return similar
+
+ def getAddressBookCollection(self, name):
+
+ # Check for public/global path
+ from twistedcaldav.storebridge import (
+ AddressBookCollectionResource,
+ ProtoAddressBookCollectionResource,
+ GlobalAddressBookCollectionResource,
+ ProtoGlobalAddressBookCollectionResource,
+ )
+ mainCls = AddressBookCollectionResource
+ protoCls = ProtoAddressBookCollectionResource
+ if isinstance(self.record, InternalDirectoryRecord):
+ if "global" in self.record.shortNames:
+ mainCls = GlobalAddressBookCollectionResource
+ protoCls = ProtoGlobalAddressBookCollectionResource
+
+ newAddressBook = self._newStoreAddressBookHome.addressbookWithName(name)
+ if newAddressBook is None:
+ # Local imports.due to circular dependency between modules.
+ similar = protoCls(
+ self._newStoreAddressBookHome,
+ principalCollections=self.principalCollections()
+ )
+ else:
+ similar = mainCls(
+ newAddressBook, self._newStoreAddressBookHome,
+ principalCollections=self.principalCollections()
+ )
+ self.propagateTransaction(similar)
+ similar.clientNotifier = self.clientNotifier.clone(similar,
+ label="collection")
+ return similar
+
+ def getChild(self, name):
+ # This avoids finding case variants of put children on case-insensitive filesystems.
+ 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
+
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ children = set(self.putChildren.keys())
+ children.update(self._newStoreAddressBookHome.listAddressbooks())
+ return children
+
+ def readProperty(self, property, request):
+ if type(property) is tuple:
+ qname = property
+ else:
+ qname = property.qname()
+
+ if qname == (customxml.calendarserver_namespace, "push-transports"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if (pubSubConfiguration['enabled'] and
+ getattr(self, "clientNotifier", None) is not None):
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ children = []
+ if pubSubConfiguration['aps-bundle-id']:
+ children.append(
+ customxml.PubSubTransportProperty(
+ customxml.PubSubSubscriptionProperty(
+ davxml.HRef(
+ pubSubConfiguration['subscription-url']
+ ),
+ ),
+ customxml.PubSubAPSBundleIDProperty(
+ pubSubConfiguration['aps-bundle-id']
+ ),
+ type="APSD",
+ )
+ )
+ if pubSubConfiguration['xmpp-server']:
+ children.append(
+ customxml.PubSubTransportProperty(
+ customxml.PubSubXMPPServerProperty(
+ pubSubConfiguration['xmpp-server']
+ ),
+ customxml.PubSubXMPPURIProperty(
+ getPubSubXMPPURI(id, pubSubConfiguration)
+ ),
+ type="XMPP",
+ )
+ )
+
+ propVal = customxml.PubSubPushTransportsProperty(*children)
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the value
+ d.addBoth(lambda ignored: propVal)
+ return d
+
+
+ else:
+ return succeed(customxml.PubSubPushTransportsProperty())
+
+ if qname == (customxml.calendarserver_namespace, "pushkey"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ if getattr(self, "clientNotifier", None) is not None:
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ propVal = customxml.PubSubXMPPPushKeyProperty(nodeName)
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the xmpp-uri value
+ d.addBoth(lambda ignored: propVal)
+ return d
+ else:
+ return succeed(customxml.PubSubXMPPPushKeyProperty())
+
+
+ if qname == (customxml.calendarserver_namespace, "xmpp-uri"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ if getattr(self, "clientNotifier", None) is not None:
+ id = self.clientNotifier.getID()
+ nodeName = getPubSubPath(id, pubSubConfiguration)
+ propVal = customxml.PubSubXMPPURIProperty(
+ getPubSubXMPPURI(id, pubSubConfiguration))
+ nodeCacher = getNodeCacher()
+ d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
+ # In either case we're going to return the xmpp-uri value
+ d.addBoth(lambda ignored: propVal)
+ return d
+ else:
+ return succeed(customxml.PubSubXMPPURIProperty())
+
+ elif qname == (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ return succeed(
+ customxml.PubSubHeartbeatProperty(
+ customxml.PubSubHeartbeatURIProperty(
+ getPubSubHeartbeatURI(pubSubConfiguration)
+ ),
+ customxml.PubSubHeartbeatMinutesProperty(
+ str(pubSubConfiguration['heartrate'])
+ )
+ )
+ )
+ else:
+ return succeed(customxml.PubSubHeartbeatURIProperty())
+
+ elif qname == (customxml.calendarserver_namespace, "xmpp-server"):
+ pubSubConfiguration = getPubSubConfiguration(config)
+ if pubSubConfiguration['enabled']:
+ return succeed(customxml.PubSubXMPPServerProperty(
+ pubSubConfiguration['xmpp-server']))
+ else:
+ return succeed(customxml.PubSubXMPPServerProperty())
+
+ return super(AddressBookHomeResource, self).readProperty(property, request)
+
+ http_ACL = None # ACL method not supported
+
+class GlobalAddressBookResource (ReadOnlyResourceMixIn, CalDAVResource):
+ """
+ Global address book. All we care about is making sure permissions are setup.
+ """
+
+ def resourceType(self):
+ return davxml.ResourceType.sharedaddressbook
+
+ def defaultAccessControlList(self):
+
+ aces = (
+ davxml.ACE(
+ davxml.Principal(davxml.Authenticated()),
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ davxml.Privilege(davxml.Write()),
+ ),
+ davxml.Protected(),
+ TwistedACLInheritable(),
+ ),
+ )
+
+ if config.GlobalAddressBook.EnableAnonymousReadAccess:
+ aces += (
+ davxml.ACE(
+ davxml.Principal(davxml.Unauthenticated()),
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ ),
+ davxml.Protected(),
+ TwistedACLInheritable(),
+ ),
+ )
+ return davxml.ACL(*aces)
+
+ def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
+ # Permissions here are fixed, and are not subject to inheritance rules, etc.
+ return succeed(self.defaultAccessControlList())
+
+
class AuthenticationWrapper(SuperAuthenticationWrapper):
""" AuthenticationWrapper implementation which allows overriding
@@ -1692,3 +2333,4 @@
return False
else:
return resource.isAddressBookCollection()
+
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/schedule.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -25,7 +25,7 @@
"IScheduleInboxResource",
]
-from twext.web2.dav.http import ErrorResponse
+from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twext.web2 import responsecode
@@ -33,6 +33,7 @@
from twext.web2.dav.element.extensions import SyncCollection
from twext.web2.dav.element.rfc2518 import HRef
from twext.web2.dav.noneprops import NonePropertyStore
+from twext.web2.dav.resource import davPrivilegeSet
from twext.web2.dav.util import joinURL, normalizeURL
from twext.web2.http import HTTPError
from twext.web2.http import Response
@@ -43,15 +44,51 @@
CalendarFreeBusySet, ScheduleCalendarTransp
from twistedcaldav.config import config
from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.extensions import DAVResource
-from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn,\
- deliverSchedulePrivilegeSet
+from twistedcaldav.extensions import DAVResource, DAVResourceWithChildrenMixin
+from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn
from twistedcaldav.resource import isCalendarCollectionResource
from twistedcaldav.scheduling.scheduler import CalDAVScheduler, IScheduleScheduler
from txdav.propertystore.base import PropertyName
-class CalendarSchedulingCollectionResource (CalDAVResource):
+def _schedulePrivilegeSet(deliver):
+ 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 = list(supported_privilege.childrenOfType(davxml.SupportedPrivilege))
+ all_supported_privileges.append(
+ davxml.SupportedPrivilege(
+ davxml.Privilege(caldavxml.ScheduleDeliver() if deliver else caldavxml.ScheduleSend()),
+ davxml.Description("schedule privileges for current principal", **{"xml:lang": "en"}),
+ ),
+ )
+ if config.Scheduling.CalDAV.OldDraftCompatibility:
+ all_supported_privileges.append(
+ davxml.SupportedPrivilege(
+ davxml.Privilege(caldavxml.Schedule()),
+ davxml.Description("old-style schedule privileges for current principal", **{"xml:lang": "en"}),
+ ),
+ )
+ top_supported_privileges.append(
+ davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
+ )
+ edited = True
+ 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 schedulePrivilegeSet"
+
+ return davxml.SupportedPrivilegeSet(*top_supported_privileges)
+
+deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
+sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
+
+class CalendarSchedulingCollectionResource (CalDAVResource, DAVResourceWithChildrenMixin):
"""
CalDAV principal resource.
@@ -65,6 +102,7 @@
assert parent is not None
CalDAVResource.__init__(self, principalCollections=parent.principalCollections())
+ DAVResourceWithChildrenMixin.__init__(self)
self.parent = parent
@@ -101,25 +139,9 @@
(caldav_namespace, "schedule-default-calendar-URL"),
)
- def resourceType(self, request):
- return succeed(davxml.ResourceType.scheduleInbox)
+ def resourceType(self):
+ return davxml.ResourceType.scheduleInbox
- def defaultAccessControlList(self):
-
- privs = (
- davxml.Privilege(caldavxml.ScheduleDeliver()),
- )
- if config.Scheduling.CalDAV.OldDraftCompatibility:
- privs += (davxml.Privilege(caldavxml.Schedule()),)
-
- return davxml.ACL(
- # CalDAV:schedule-deliver for any authenticated user
- davxml.ACE(
- davxml.Principal(davxml.Authenticated()),
- davxml.Grant(*privs),
- ),
- )
-
@inlineCallbacks
def readProperty(self, property, request):
if type(property) is tuple:
@@ -259,6 +281,29 @@
davxml.HRef(defaultCalendarURL))
)
+ ##
+ # ACL
+ ##
+
+ def supportedPrivileges(self, request):
+ return succeed(deliverSchedulePrivilegeSet)
+
+ def defaultAccessControlList(self):
+
+ privs = (
+ davxml.Privilege(caldavxml.ScheduleDeliver()),
+ )
+ if config.Scheduling.CalDAV.OldDraftCompatibility:
+ privs += (davxml.Privilege(caldavxml.Schedule()),)
+
+ return davxml.ACL(
+ # CalDAV:schedule-deliver for any authenticated user
+ davxml.ACE(
+ davxml.Principal(davxml.Authenticated()),
+ davxml.Grant(*privs),
+ ),
+ )
+
class ScheduleOutboxResource (CalendarSchedulingCollectionResource):
"""
CalDAV schedule Outbox resource.
@@ -266,6 +311,37 @@
Extends L{DAVResource} to provide CalDAV functionality.
"""
+ def resourceType(self):
+ return davxml.ResourceType.scheduleOutbox
+
+ @inlineCallbacks
+ def http_POST(self, request):
+ """
+ The CalDAV POST method.
+
+ This uses a generator function yielding either L{waitForDeferred} objects or L{Response} objects.
+ This allows for code that follows a 'linear' execution pattern rather than having to use nested
+ L{Deferred} callbacks. The logic is easier to follow this way plus we don't run into deep nesting
+ issues which the other approach would have with large numbers of recipients.
+ """
+ # Check authentication and access controls
+ yield self.authorize(request, (caldavxml.ScheduleSend(),))
+
+ # This is a local CALDAV scheduling operation.
+ scheduler = CalDAVScheduler(request, self)
+
+ # Do the POST processing treating
+ result = (yield scheduler.doSchedulingViaPOST())
+ returnValue(result.response())
+
+
+ ##
+ # ACL
+ ##
+
+ def supportedPrivileges(self, request):
+ return succeed(sendSchedulePrivilegeSet)
+
def defaultAccessControlList(self):
if config.EnableProxyPrincipals:
myPrincipal = self.parent.principalForRecord()
@@ -287,29 +363,13 @@
else:
return super(ScheduleOutboxResource, self).defaultAccessControlList()
- def resourceType(self, request):
- return succeed(davxml.ResourceType.scheduleOutbox)
+ def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
+ return succeed(MultiStatusResponse(()))
+
+ def report_urn_ietf_params_xml_ns_caldav_calendar_multiget(self, request, multiget):
+ responses = [davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)) for href in multiget.resources]
+ return succeed(MultiStatusResponse((responses)))
- @inlineCallbacks
- def http_POST(self, request):
- """
- The CalDAV POST method.
-
- This uses a generator function yielding either L{waitForDeferred} objects or L{Response} objects.
- This allows for code that follows a 'linear' execution pattern rather than having to use nested
- L{Deferred} callbacks. The logic is easier to follow this way plus we don't run into deep nesting
- issues which the other approach would have with large numbers of recipients.
- """
- # Check authentication and access controls
- yield self.authorize(request, (caldavxml.ScheduleSend(),))
-
- # This is a local CALDAV scheduling operation.
- scheduler = CalDAVScheduler(request, self)
-
- # Do the POST processing treating
- result = (yield scheduler.doSchedulingViaPOST())
- returnValue(result.response())
-
class IScheduleInboxResource (ReadOnlyNoCopyResourceMixIn, DAVResource):
"""
iSchedule Inbox resource.
@@ -338,8 +398,8 @@
def checkPreconditions(self, request):
return None
- def resourceType(self, request):
- return succeed(davxml.ResourceType.ischeduleinbox)
+ def resourceType(self):
+ return davxml.ResourceType.ischeduleinbox
def isCollection(self):
return False
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/utils.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/utils.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/scheduling/utils.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twistedcaldav.method import report_common
from twext.web2.dav.util import joinURL
@@ -40,12 +40,10 @@
request._rememberResource(calendar_home, calendar_home.url())
# Run a UID query against the UID
- @inlineCallbacks
def queryCalendarCollection(collection, uri):
if not allow_shared:
- isvirt = (yield collection.isVirtualShare(request))
- if isvirt:
- returnValue(True)
+ if collection.isVirtualShare():
+ return succeed(True)
rname = collection.index().resourceNameForUID(uid)
if rname:
@@ -56,9 +54,9 @@
result["resource_name"] = rname
result["calendar_collection"] = collection
result["calendar_collection_uri"] = uri
- returnValue(False)
+ return succeed(False)
else:
- returnValue(True)
+ return succeed(True)
# NB We are by-passing privilege checking here. That should be OK as the data found is not
# exposed to the user.
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/sharing.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -66,26 +66,23 @@
return None
return self.isShared(request).addCallback(sharedOK)
- @inlineCallbacks
- def upgradeToShare(self, request):
+ def upgradeToShare(self):
""" Upgrade this collection to a shared state """
# Change resourcetype
- rtype = (yield self.resourceType(request))
+ rtype = self.resourceType()
rtype = davxml.ResourceType(*(rtype.children + (customxml.SharedOwner(),)))
self.writeDeadProperty(rtype)
# Create invites database
self.invitesDB().create()
-
- returnValue(True)
@inlineCallbacks
def downgradeFromShare(self, request):
# Change resource type (note this might be called after deleting a resource
# so we have to cope with that)
- rtype = (yield self.resourceType(request))
+ rtype = self.resourceType()
rtype = davxml.ResourceType(*([child for child in rtype.children if child != customxml.SharedOwner()]))
self.writeDeadProperty(rtype)
@@ -212,9 +209,9 @@
elif hasattr(self, "_newStoreAddressBook"):
self._newStoreAddressBook.setSharingUID(self._shareePrincipal.principalUID())
- def isVirtualShare(self, request):
+ def isVirtualShare(self):
""" Return True if this is a shared calendar collection """
- return succeed(hasattr(self, "_isVirtualShare"))
+ return hasattr(self, "_isVirtualShare")
def removeVirtualShare(self, request):
""" Return True if this is a shared calendar collection """
@@ -226,17 +223,16 @@
shareeHome = self._shareePrincipal.addressBookHome(request)
return shareeHome.removeShare(request, self._share)
- @inlineCallbacks
- def resourceType(self, request):
+ def resourceType(self):
superObject = super(SharedCollectionMixin, self)
try:
superMethod = superObject.resourceType
except AttributeError:
rtype = davxml.ResourceType()
else:
- rtype = (yield superMethod(request))
+ rtype = superMethod()
- isVirt = (yield self.isVirtualShare(request))
+ isVirt = self.isVirtualShare()
if isVirt:
rtype = davxml.ResourceType(
*(
@@ -244,7 +240,7 @@
(customxml.Shared(),)
)
)
- returnValue(rtype)
+ return rtype
def sharedResourceType(self):
"""
@@ -635,10 +631,7 @@
def _autoShare(isShared, request):
if not isShared:
- return self.upgradeToShare(request)
- else:
- return succeed(True)
- raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Cannot upgrade to shared calendar"))
+ self.upgradeToShare()
@inlineCallbacks
def _processInviteDoc(_, request):
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/static.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twistedcaldav.index import Index
"""
CalDAV-aware static resources.
@@ -22,85 +23,43 @@
__all__ = [
"CalDAVFile",
"AutoProvisioningFileMixIn",
- "CalendarHomeProvisioningFile",
- "CalendarHomeUIDProvisioningFile",
- "CalendarHomeFile",
- "ScheduleFile",
- "ScheduleInboxFile",
- "ScheduleOutboxFile",
- "DropBoxHomeFile",
- "DropBoxCollectionFile",
- "DropBoxChildFile",
- "AddressBookHomeProvisioningFile",
- "AddressBookHomeUIDProvisioningFile",
- "AddressBookHomeFile",
"DirectoryBackedAddressBookFile",
- "GlobalAddressBookFile",
]
-import datetime
import os
import errno
from urlparse import urlsplit
-from uuid import uuid4
from twext.python.log import Logger
from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue, maybeDeferred
-from twisted.python.failure import Failure
-from twext.python.filepath import CachingFilePath as FilePath
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.element.base import dav_namespace
from twext.web2.dav.fileop import mkcollection, rmdir
-from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
+from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.idav import IDAVResource
-from twext.web2.dav.noneprops import NonePropertyStore
from twext.web2.dav.resource import AccessDeniedError
from twext.web2.dav.resource import davPrivilegeSet
-from twext.web2.dav.util import parentForURL, bindMethods, joinURL
+from twext.web2.dav.util import parentForURL, bindMethods
from twistedcaldav import caldavxml
from twistedcaldav import carddavxml
-from twistedcaldav import customxml
from twistedcaldav.caldavxml import caldav_namespace
-from twistedcaldav.client.reverseproxy import ReverseProxyResource
from twistedcaldav.config import config
from twistedcaldav.customxml import TwistedCalendarAccessProperty, TwistedScheduleMatchETags
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
-from twistedcaldav.directory.internal import InternalDirectoryRecord
-from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
-from twistedcaldav.linkresource import LinkResource, LinkFollowerMixIn
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+from twistedcaldav.linkresource import LinkFollowerMixIn
-from twistedcaldav.freebusyurl import FreeBusyURLResource
from twistedcaldav.ical import Component as iComponent
from twistedcaldav.ical import Property as iProperty
-from twistedcaldav.index import Index, IndexSchedule, SyncTokenValidException
from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource, isPseudoCalendarCollectionResource
from twistedcaldav.resource import isAddressBookCollectionResource
-from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
from twistedcaldav.directorybackedaddressbook import DirectoryBackedAddressBookResource
-from twistedcaldav.directory.addressbook import uidsResourceName as uidsResourceNameAddressBook,\
- GlobalAddressBookResource
-from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeProvisioningResource
-from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeTypeProvisioningResource
-from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeUIDProvisioningResource
-from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeResource
-from twistedcaldav.directory.calendar import uidsResourceName
-from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
-from twistedcaldav.directory.calendar import DirectoryCalendarHomeTypeProvisioningResource
-from twistedcaldav.directory.calendar import DirectoryCalendarHomeUIDProvisioningResource
-from twistedcaldav.directory.calendar import DirectoryCalendarHomeResource
from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn
-from twistedcaldav.sharing import SharedHomeMixin
from twistedcaldav.vcardindex import AddressBookIndex
-from twistedcaldav.notify import getPubSubConfiguration, getPubSubXMPPURI
-from twistedcaldav.notify import getPubSubHeartbeatURI, getPubSubPath
-from twistedcaldav.notify import ClientNotifier, getNodeCacher
log = Logger()
@@ -290,7 +249,7 @@
# FIXME: move cache implementation!
# Determine the cache key
-# isvirt = (yield self.isVirtualShare(request))
+# isvirt = self.isVirtualShare()
# if isvirt:
# principal = (yield self.resourceOwnerPrincipal(request))
# if principal:
@@ -549,111 +508,6 @@
else:
return Index(self)
- def whatchanged(self, client_token):
-
- current_token = str(self.readDeadProperty(customxml.GETCTag))
- current_uuid, current_revision = current_token.split("#", 1)
- current_revision = int(current_revision)
-
- if client_token:
- try:
- caluuid, revision = client_token.split("#", 1)
- revision = int(revision)
-
- # Check client token validity
- if caluuid != current_uuid:
- raise ValueError
- if revision > current_revision:
- raise ValueError
- except ValueError:
- raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
- else:
- revision = 0
-
- try:
- changed, removed = self.index().whatchanged(revision)
- except SyncTokenValidException:
- raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
-
- return changed, removed, current_token
-
- @inlineCallbacks
- def bumpSyncToken(self):
- """
- Increment the sync-token which is also the ctag.
-
- return: a deferred that returns the new revision number
- """
- assert self.isCollection()
-
- # Need to lock
- lock = MemcacheLock("ResourceLock", self.fp.path, timeout=60.0)
- try:
- try:
- yield lock.acquire()
- except MemcacheLockTimeoutError:
- raise HTTPError(StatusResponse(responsecode.CONFLICT, "Resource: %s currently in use on the server." % (self.uri,)))
-
- try:
- token = str(self.readDeadProperty(customxml.GETCTag))
- caluuid, revision = token.split("#", 1)
- revision = int(revision) + 1
- token = "%s#%d" % (caluuid, revision,)
-
- except (HTTPError, ValueError):
- # Initialise it
- caluuid = uuid4()
- revision = 1
- token = "%s#%d" % (caluuid, revision,)
-
- yield self.updateCTag(token)
- returnValue(revision)
- finally:
- yield lock.clean()
-
- def initSyncToken(self):
- """
- Create a new sync-token which is also the ctag.
- """
- # FIXME: new implementation is in txcaldav.file, this should be
- # deleted.
- assert self.isCollection()
- # Initialise it
- caluuid = uuid4()
- revision = 1
- token = "%s#%d" % (caluuid, revision,)
- try:
- self.writeDeadProperty(customxml.GETCTag(token))
- except:
- return fail(Failure())
-
- def getSyncToken(self):
- """
- Return current sync-token value.
- """
- assert self.isCollection()
-
- return str(self.readDeadProperty(customxml.GETCTag))
-
- def updateCTag(self, token=None):
- assert self.isCollection()
-
- if not token:
- token = str(datetime.datetime.now())
- try:
- self.writeDeadProperty(customxml.GETCTag(token))
- except:
- return fail(Failure())
-
- if hasattr(self, 'clientNotifier'):
- self.clientNotifier.notify(op="update")
-
- return succeed(True)
-
- ##
- # File
- ##
-
def listChildren(self):
return [
child for child in super(CalDAVFile, self).listChildren()
@@ -830,889 +684,6 @@
super(AutoProvisioningFileMixIn, self)._initTypeAndEncoding()
-class CalendarHomeProvisioningFile(AutoProvisioningFileMixIn,
- DirectoryCalendarHomeProvisioningResource,
- DAVFile):
- """
- Resource which provisions calendar home collections as needed.
- """
-
- def __init__(self, path, directory, url, store):
- """
- Initialize this L{CalendarHomeProvisioningFile}.
-
- @param path: the path to the filesystem directory which will back the
- resource.
-
- @type path: L{FilePath}
-
- @param directory: an L{IDirectoryService} to provision calendars from.
-
- @param url: the canonical URL for this L{CalendarHomeProvisioningFile}
- resource.
- """
- DAVFile.__init__(self, path)
- DirectoryCalendarHomeProvisioningResource.__init__(self, directory, url)
- self._newStore = store
-
-
- def provisionChild(self, name):
- if name == uidsResourceName:
- return CalendarHomeUIDProvisioningFile(self.fp.child(name).path, self)
-
- return CalendarHomeTypeProvisioningFile(self.fp.child(name).path, self, name)
-
-
- def createSimilarFile(self, path):
- raise HTTPError(responsecode.NOT_FOUND)
-
-
-
-class CalendarHomeTypeProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeTypeProvisioningResource, DAVFile):
- def __init__(self, path, parent, recordType):
- """
- @param path: the path to the file which will back the resource.
- @param parent: the parent of this resource
- @param recordType: the directory record type to provision.
- """
- DAVFile.__init__(self, path)
- DirectoryCalendarHomeTypeProvisioningResource.__init__(self, parent, recordType)
-
-
-
-class CalendarHomeUIDProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeUIDProvisioningResource, DAVFile):
- def __init__(self, path, parent, homeResourceClass=None):
- """
- @param path: the path to the file which will back the resource.
- """
- DAVFile.__init__(self, path)
- DirectoryCalendarHomeUIDProvisioningResource.__init__(self, parent)
- if homeResourceClass is None:
- self.homeResourceClass = CalendarHomeFile
- else:
- self.homeResourceClass = homeResourceClass
-
-
- def locateChild(self, request, segments):
-
- name = segments[0]
- if name == "":
- return (self, ())
-
- record = self.directory.recordWithUID(name)
- if record:
- return (self.homeResourceForRecord(record, request), segments[1:])
- else:
- return (None, ())
-
- def homeResourceForRecord(self, record, request):
- self.provision()
- TRANSACTION_KEY = '_newStoreTransaction'
- transaction = getattr(request, TRANSACTION_KEY, None)
- if transaction is None:
- transaction = self.parent._newStore.newTransaction(repr(request))
- setattr(request, TRANSACTION_KEY, transaction)
-
- name = record.uid
-
- if record is None:
- log.msg("No directory record with GUID %r" % (name,))
- return None
-
- if not record.enabledForCalendaring:
- log.msg("Directory record %r is not enabled for calendaring" % (record,))
- return None
-
- assert len(name) > 4, "Directory record has an invalid GUID: %r" % (name,)
-
- if record.locallyHosted():
- childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
- child = self.homeResourceClass(childPath.path, self, record, transaction)
-
- if not child.exists():
- self.provision()
-
- if not childPath.parent().isdir():
- childPath.parent().makedirs()
-
- for oldPath in (
- # Pre 2.0: All in one directory
- self.fp.child(name),
- # Pre 1.2: In types hierarchy instead of the GUID hierarchy
- self.parent.getChild(record.recordType).fp.child(record.shortNames[0]),
- ):
- if oldPath.exists():
- # The child exists at an old location. Move to new location.
- log.msg("Moving calendar home from old location %r to new location %r." % (oldPath, childPath))
- try:
- oldPath.moveTo(childPath)
- except (OSError, IOError), e:
- log.err("Error moving calendar home %r: %s" % (oldPath, e))
- raise HTTPError(StatusResponse(
- responsecode.INTERNAL_SERVER_ERROR,
- "Unable to move calendar home."
- ))
- child.fp.changed()
- break
-
- assert child.exists()
-
- else:
- childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
- child = CalendarHomeReverseProxyFile(childPath.path, self, record)
-
- return child
-
- def createSimilarFile(self, path):
- raise HTTPError(responsecode.NOT_FOUND)
-
-class CalendarHomeReverseProxyFile(ReverseProxyResource):
-
- def __init__(self, path, parent, record):
- self.path = path
- self.parent = parent
- self.record = record
-
- super(CalendarHomeReverseProxyFile, self).__init__(self.record.hostedAt)
-
- def url(self):
- return joinURL(self.parent.url(), self.record.uid)
-
-class CalendarHomeFile(AutoProvisioningFileMixIn, SharedHomeMixin,
- DirectoryCalendarHomeResource, CalDAVFile):
- """
- Calendar home collection resource.
- """
- def liveProperties(self):
-
- return super(CalendarHomeFile, self).liveProperties() + (
- (customxml.calendarserver_namespace, "push-transports"),
- (customxml.calendarserver_namespace, "pushkey"),
- (customxml.calendarserver_namespace, "xmpp-uri"),
- (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"),
- (customxml.calendarserver_namespace, "xmpp-server"),
- )
-
- def __init__(self, path, parent, record, transaction):
- """
- @param path: the path to the file which will back the resource.
- """
-
- self.associateWithTransaction(transaction)
-
- # TODO: when calendar home gets a resourceID( ) method, remove
- # the "id=record.uid" keyword from this call:
- self.clientNotifier = ClientNotifier(self, id=record.uid)
- storeHome = transaction.calendarHomeWithUID(record.uid)
- if storeHome is not None:
- created = False
- else:
- storeHome = transaction.calendarHomeWithUID(
- record.uid, create=True
- )
- created = True
- self._newStoreCalendarHome = storeHome
- CalDAVFile.__init__(self, path)
- DirectoryCalendarHomeResource.__init__(self, parent, record)
- from twistedcaldav.storebridge import _NewStorePropertiesWrapper
- self._dead_properties = _NewStorePropertiesWrapper(
- self._newStoreCalendarHome.properties()
- )
- if created:
- # This is a bit of a hack. Really we ought to be always generating
- # this URL live from a back-end method that tells us what the
- # default calendar is.
- inbox = self.getChild("inbox")
- childURL = joinURL(self.url(), "calendar")
- inbox.processFreeBusyCalendar(childURL, True)
-
-
- def sharesDB(self):
- """
- Retrieve the new-style shares DB wrapper.
- """
- if not hasattr(self, "_sharesDB"):
- self._sharesDB = self._newStoreCalendarHome.retrieveOldShares()
- return self._sharesDB
-
-
- def exists(self):
- # FIXME: tests
- return True
-
-
- def quotaSize(self, request):
- # FIXME: tests, workingness
- return succeed(0)
-
-
- def provision(self):
- if config.Sharing.Enabled and config.Sharing.Calendars.Enabled and self.fp.exists():
- self.provisionShares()
- return
-
- def provisionChild(self, name):
- from twistedcaldav.storebridge import StoreScheduleInboxFile
- from twistedcaldav.storebridge import DropboxCollection
- if config.EnableDropBox:
- DropBoxHomeFileClass = DropboxCollection
- else:
- DropBoxHomeFileClass = None
-
- if config.FreeBusyURL.Enabled:
- FreeBusyURLResourceClass = FreeBusyURLResource
- else:
- FreeBusyURLResourceClass = None
-
- # For storebridge stuff we special case this
- if name == "notification" and config.Sharing.Enabled and config.Sharing.Calendars.Enabled:
- return self.createNotificationsFile(self.fp.child(name).path)
-
- cls, isFileType = {
- "inbox" : (StoreScheduleInboxFile, True,),
- "outbox" : (ScheduleOutboxFile, True,),
- "dropbox" : (DropBoxHomeFileClass, True,),
- "freebusy" : (FreeBusyURLResourceClass, False,),
- }.get(name, (None, True,))
-
- if cls is not None:
- if isFileType:
- child = cls(self.fp.child(name).path, self)
- else:
- child = cls(self)
- child.clientNotifier = self.clientNotifier.clone(child,
- label="collection")
- return child
- return self.createSimilarFile(self.fp.child(name).path)
-
- def createNotificationsFile(self, path):
-
- txn = self._newStoreCalendarHome._transaction
- notifications = txn.notificationsWithUID(self._newStoreCalendarHome.uid())
-
- from twistedcaldav.storebridge import NotificationCollectionFile
- similar = NotificationCollectionFile(
- notifications,
- self._newStoreCalendarHome,
- principalCollections = self.principalCollections(),
- )
- self.propagateTransaction(similar)
- similar.clientNotifier = self.clientNotifier.clone(similar,
- label="collection")
- return similar
-
- def createSimilarFile(self, path):
-
- if self.comparePath(path):
- return self
- else:
- if not isinstance(path, FilePath):
- path = FilePath(path)
- newCalendar = self._newStoreCalendarHome.calendarWithName(
- path.basename()
- )
- if newCalendar is None:
- # Local imports.due to circular dependency between modules.
- from twistedcaldav.storebridge import (
- ProtoCalendarCollectionFile)
- similar = ProtoCalendarCollectionFile(
- self._newStoreCalendarHome,
- path, principalCollections=self.principalCollections()
- )
- else:
- from twistedcaldav.storebridge import CalendarCollectionFile
- similar = CalendarCollectionFile(
- newCalendar, self._newStoreCalendarHome,
- path, principalCollections=self.principalCollections()
- )
- self.propagateTransaction(similar)
- similar.clientNotifier = self.clientNotifier.clone(similar,
- label="collection")
- return similar
-
- def getChild(self, name):
- # This avoids finding case variants of put children on case-insensitive filesystems.
- if name not in self.putChildren and name.lower() in (x.lower() for x in self.putChildren):
- return None
-
- return super(CalendarHomeFile, self).getChild(name)
-
-
- def readProperty(self, property, request):
- if type(property) is tuple:
- qname = property
- else:
- qname = property.qname()
-
- if qname == (customxml.calendarserver_namespace, "push-transports"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if (pubSubConfiguration['enabled'] and
- getattr(self, "clientNotifier", None) is not None):
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- children = []
- if pubSubConfiguration['aps-bundle-id']:
- children.append(
- customxml.PubSubTransportProperty(
- customxml.PubSubSubscriptionProperty(
- davxml.HRef(
- pubSubConfiguration['subscription-url']
- ),
- ),
- customxml.PubSubAPSBundleIDProperty(
- pubSubConfiguration['aps-bundle-id']
- ),
- type="APSD",
- )
- )
- if pubSubConfiguration['xmpp-server']:
- children.append(
- customxml.PubSubTransportProperty(
- customxml.PubSubXMPPServerProperty(
- pubSubConfiguration['xmpp-server']
- ),
- customxml.PubSubXMPPURIProperty(
- getPubSubXMPPURI(id, pubSubConfiguration)
- ),
- type="XMPP",
- )
- )
-
- propVal = customxml.PubSubPushTransportsProperty(*children)
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the value
- d.addBoth(lambda ignored: propVal)
- return d
-
-
- else:
- return succeed(customxml.PubSubPushTransportsProperty())
-
- if qname == (customxml.calendarserver_namespace, "pushkey"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- if getattr(self, "clientNotifier", None) is not None:
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- propVal = customxml.PubSubXMPPPushKeyProperty(nodeName)
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the xmpp-uri value
- d.addBoth(lambda ignored: propVal)
- return d
- else:
- return succeed(customxml.PubSubXMPPPushKeyProperty())
-
-
- if qname == (customxml.calendarserver_namespace, "xmpp-uri"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- if getattr(self, "clientNotifier", None) is not None:
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- propVal = customxml.PubSubXMPPURIProperty(
- getPubSubXMPPURI(id, pubSubConfiguration))
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the xmpp-uri value
- d.addBoth(lambda ignored: propVal)
- return d
- else:
- return succeed(customxml.PubSubXMPPURIProperty())
-
- elif qname == (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- return succeed(
- customxml.PubSubHeartbeatProperty(
- customxml.PubSubHeartbeatURIProperty(
- getPubSubHeartbeatURI(pubSubConfiguration)
- ),
- customxml.PubSubHeartbeatMinutesProperty(
- str(pubSubConfiguration['heartrate'])
- )
- )
- )
- else:
- return succeed(customxml.PubSubHeartbeatURIProperty())
-
- elif qname == (customxml.calendarserver_namespace, "xmpp-server"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- return succeed(customxml.PubSubXMPPServerProperty(
- pubSubConfiguration['xmpp-server']))
- else:
- return succeed(customxml.PubSubXMPPServerProperty())
-
- return super(CalendarHomeFile, self).readProperty(property, request)
-
-
-class ScheduleFile (ReadOnlyResourceMixIn, AutoProvisioningFileMixIn, CalDAVFile):
- def __init__(self, path, parent):
- super(ScheduleFile, self).__init__(path, principalCollections=parent.principalCollections())
-
- def isCollection(self):
- return True
-
- def createSimilarFile(self, path):
- if self.comparePath(path):
- return self
- else:
- return CalDAVFile(path, principalCollections=self.principalCollections())
-
- def index(self):
- """
- Obtains the index for an schedule collection resource.
- @return: the index object for this resource.
- @raise AssertionError: if this resource is not a calendar collection
- resource.
- """
- return IndexSchedule(self)
-
-class ScheduleInboxFile (ScheduleInboxResource, ScheduleFile):
- """
- Calendar scheduling inbox collection resource.
- """
- def __init__(self, path, parent):
- ScheduleFile.__init__(self, path, parent)
- ScheduleInboxResource.__init__(self, parent)
-
- def __repr__(self):
- return "<%s (calendar inbox collection): %s>" % (self.__class__.__name__, self.fp.path)
-
-
- ##
- # ACL
- ##
-
- def supportedPrivileges(self, request):
- return succeed(deliverSchedulePrivilegeSet)
-
-class ScheduleOutboxFile (ScheduleOutboxResource, ScheduleFile):
- """
- Calendar scheduling outbox collection resource.
- """
- def __init__(self, path, parent):
- ScheduleFile.__init__(self, NotFilePath(isdir=True), parent)
- ScheduleOutboxResource.__init__(self, parent)
-
- def deadProperties(self):
- if not hasattr(self, "_dead_properties"):
- self._dead_properties = NonePropertyStore(self)
- return self._dead_properties
-
- def etag(self):
- return None
-
- def provision(self):
- """
- Schedule outboxes do not need to be provisioned; they shouldn't store
- anything.
- """
-
- def __repr__(self):
- return "<%s (calendar outbox collection): %s>" % (self.__class__.__name__, self.fp.path)
-
-
- ##
- # ACL
- ##
-
- def supportedPrivileges(self, request):
- return succeed(sendSchedulePrivilegeSet)
-
- def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
- return succeed(MultiStatusResponse(()))
-
- def report_urn_ietf_params_xml_ns_caldav_calendar_multiget(self, request, multiget):
- responses = [davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)) for href in multiget.resources]
- return succeed(MultiStatusResponse((responses)))
-
-class AddressBookHomeProvisioningFile (AutoProvisioningFileMixIn, DirectoryAddressBookHomeProvisioningResource, DAVFile):
- """
- Resource which provisions address book home collections as needed.
- """
- def __init__(self, path, directory, url, store):
- """
- @param path: the path to the file which will back the resource.
- @param directory: an L{IDirectoryService} to provision address books from.
- @param url: the canonical URL for the resource.
- """
- DAVFile.__init__(self, path)
- DirectoryAddressBookHomeProvisioningResource.__init__(self, directory, url)
- self._newStore = store
-
-
- def provisionChild(self, name):
- if name == uidsResourceNameAddressBook:
- return AddressBookHomeUIDProvisioningFile(self.fp.child(name).path, self)
-
- return AddressBookHomeTypeProvisioningFile(self.fp.child(name).path, self, name)
-
- def createSimilarFile(self, path):
- raise HTTPError(responsecode.NOT_FOUND)
-
-class AddressBookHomeTypeProvisioningFile (AutoProvisioningFileMixIn, DirectoryAddressBookHomeTypeProvisioningResource, DAVFile):
- def __init__(self, path, parent, recordType):
- """
- @param path: the path to the file which will back the resource.
- @param parent: the parent of this resource
- @param recordType: the directory record type to provision.
- """
- DAVFile.__init__(self, path)
- DirectoryAddressBookHomeTypeProvisioningResource.__init__(self, parent, recordType)
-
-class AddressBookHomeUIDProvisioningFile (AutoProvisioningFileMixIn, DirectoryAddressBookHomeUIDProvisioningResource, DAVFile):
- def __init__(self, path, parent, homeResourceClass=None):
- """
- @param path: the path to the file which will back the resource.
- """
- DAVFile.__init__(self, path)
- DirectoryAddressBookHomeUIDProvisioningResource.__init__(self, parent)
- if homeResourceClass is None:
- self.homeResourceClass = AddressBookHomeFile
- else:
- self.homeResourceClass = homeResourceClass
-
- def locateChild(self, request, segments):
-
- name = segments[0]
- if name == "":
- return (self, ())
-
- record = self.directory.recordWithUID(name)
- if record:
- return (self.homeResourceForRecord(record, request), segments[1:])
- else:
- return (None, ())
-
- def homeResourceForRecord(self, record, request):
- self.provision()
- TRANSACTION_KEY = '_newStoreTransaction'
- transaction = getattr(request, TRANSACTION_KEY, None)
- if transaction is None:
- transaction = self.parent._newStore.newTransaction(repr(request))
- setattr(request, TRANSACTION_KEY, transaction)
-
- name = record.uid
-
- if record is None:
- log.msg("No directory record with GUID %r" % (name,))
- return None
-
- if not record.enabledForAddressBooks:
- log.msg("Directory record %r is not enabled for address books" % (record,))
- return None
-
- assert len(name) > 4
-
- childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
- child = self.homeResourceClass(childPath.path, self, record, transaction)
-
- if not child.exists():
- self.provision()
-
- if not childPath.parent().isdir():
- childPath.parent().makedirs()
-
- for oldPath in (
- # Pre 2.0: All in one directory
- self.fp.child(name),
- # Pre 1.2: In types hierarchy instead of the GUID hierarchy
- self.parent.getChild(record.recordType).fp.child(record.shortNames[0]),
- ):
- if oldPath.exists():
- # The child exists at an old location. Move to new location.
- log.msg("Moving address book home from old location %r to new location %r." % (oldPath, childPath))
- try:
- oldPath.moveTo(childPath)
- except (OSError, IOError), e:
- log.err("Error moving address book home %r: %s" % (oldPath, e))
- raise HTTPError(StatusResponse(
- responsecode.INTERNAL_SERVER_ERROR,
- "Unable to move address book home."
- ))
- child.fp.restat(False)
- break
- else:
- #
- # NOTE: provisionDefaultAddressBooks() returns a deferred, which we are ignoring.
- # The result being that the default calendars will be present at some point
- # in the future, not necessarily right now, and we don't have a way to wait
- # on that to finish.
- #
- child.provisionDefaultAddressBooks()
-
- #
- # Try to work around the above a little by telling the client that something
- # when wrong temporarily if the child isn't provisioned right away.
- #
- if not child.exists():
- raise HTTPError(StatusResponse(
- responsecode.SERVICE_UNAVAILABLE,
- "Provisioning address book home."
- ))
-
- assert child.exists()
-
- return child
-
- def createSimilarFile(self, path):
- raise HTTPError(responsecode.NOT_FOUND)
-
-class AddressBookHomeFile (AutoProvisioningFileMixIn, SharedHomeMixin, DirectoryAddressBookHomeResource, CalDAVFile):
- """
- Address book home collection resource.
- """
-
- def liveProperties(self):
- return super(AddressBookHomeFile, self).liveProperties() + (
- (customxml.calendarserver_namespace, "push-transports"),
- (customxml.calendarserver_namespace, "pushkey"),
- (customxml.calendarserver_namespace, "xmpp-uri"),
- (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"),
- (customxml.calendarserver_namespace, "xmpp-server"),
- )
-
- def __init__(self, path, parent, record, transaction):
- """
- @param path: the path to the file which will back the resource.
- """
-
- self.associateWithTransaction(transaction)
-
- # TODO: when addressbook home gets a resourceID( ) method, remove
- # the "id=record.uid" keyword from this call:
- self.clientNotifier = ClientNotifier(self, id=record.uid)
- self._newStoreAddressBookHome = (
- transaction.addressbookHomeWithUID(record.uid, create=True)
- )
- CalDAVFile.__init__(self, path)
- DirectoryAddressBookHomeResource.__init__(self, parent, record)
-
- from twistedcaldav.storebridge import _NewStorePropertiesWrapper
- self._dead_properties = _NewStorePropertiesWrapper(
- self._newStoreAddressBookHome.properties()
- )
-
-
- def sharesDB(self):
- """
- Retrieve the new-style shares DB wrapper.
- """
- if not hasattr(self, "_sharesDB"):
- self._sharesDB = self._newStoreAddressBookHome.retrieveOldShares()
- return self._sharesDB
-
-
- def exists(self):
- # FIXME: tests
- return True
-
-
- def quotaSize(self, request):
- # FIXME: tests, workingness
- return succeed(0)
-
-
- def provision(self):
- if config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled:
- self.provisionShares()
- self.provisionLinks()
-
- def provisionLinks(self):
-
- if not hasattr(self, "_provisionedLinks"):
- if config.GlobalAddressBook.Enabled:
- self.putChild(
- config.GlobalAddressBook.Name,
- LinkResource(self, "/addressbooks/public/global/addressbook/"),
- )
- self._provisionedLinks = True
-
- def provisionChild(self, name):
-
- # For storebridge stuff we special case this
- if name == "notification" and config.Sharing.Enabled and config.Sharing.AddressBooks.Enabled and not config.Sharing.Calendars.Enabled:
- return self.createNotificationsFile(self.fp.child(name).path)
-
- return self.createSimilarFile(self.fp.child(name).path)
-
- def createNotificationsFile(self, path):
-
- txn = self._newStoreAddressBookHome._transaction
- notifications = txn.notificationsWithUID(self._newStoreAddressBookHome.uid())
-
- from twistedcaldav.storebridge import NotificationCollectionFile
- similar = NotificationCollectionFile(
- notifications,
- self._newStoreAddressBookHome,
- principalCollections = self.principalCollections(),
- )
- self.propagateTransaction(similar)
- similar.clientNotifier = self.clientNotifier.clone(similar,
- label="collection")
- return similar
-
- def createSimilarFile(self, path):
- if self.comparePath(path):
- return self
- else:
- if not isinstance(path, FilePath):
- path = FilePath(path)
-
- # Check for public/global path
- from twistedcaldav.storebridge import (
- AddressBookCollectionFile,
- ProtoAddressBookCollectionFile,
- GlobalAddressBookCollectionFile,
- ProtoGlobalAddressBookCollectionFile,
- )
- mainCls = AddressBookCollectionFile
- protoCls = ProtoAddressBookCollectionFile
- if isinstance(self.record, InternalDirectoryRecord):
- if "global" in self.record.shortNames:
- mainCls = GlobalAddressBookCollectionFile
- protoCls = ProtoGlobalAddressBookCollectionFile
-
- newAddressBook = self._newStoreAddressBookHome.addressbookWithName(
- path.basename()
- )
- if newAddressBook is None:
- # Local imports.due to circular dependency between modules.
- similar = protoCls(
- self._newStoreAddressBookHome,
- path, principalCollections=self.principalCollections()
- )
- else:
- similar = mainCls(
- newAddressBook, self._newStoreAddressBookHome,
- path, principalCollections=self.principalCollections()
- )
- self.propagateTransaction(similar)
- similar.clientNotifier = self.clientNotifier.clone(similar,
- label="collection")
- return similar
-
- def getChild(self, name):
- # This avoids finding case variants of put children on case-insensitive filesystems.
- if name not in self.putChildren and name.lower() in (x.lower() for x in self.putChildren):
- return None
-
- return super(AddressBookHomeFile, self).getChild(name)
-
-
- def readProperty(self, property, request):
- if type(property) is tuple:
- qname = property
- else:
- qname = property.qname()
-
- if qname == (customxml.calendarserver_namespace, "push-transports"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if (pubSubConfiguration['enabled'] and
- getattr(self, "clientNotifier", None) is not None):
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- children = []
- if pubSubConfiguration['aps-bundle-id']:
- children.append(
- customxml.PubSubTransportProperty(
- customxml.PubSubSubscriptionProperty(
- davxml.HRef(
- pubSubConfiguration['subscription-url']
- ),
- ),
- customxml.PubSubAPSBundleIDProperty(
- pubSubConfiguration['aps-bundle-id']
- ),
- type="APSD",
- )
- )
- if pubSubConfiguration['xmpp-server']:
- children.append(
- customxml.PubSubTransportProperty(
- customxml.PubSubXMPPServerProperty(
- pubSubConfiguration['xmpp-server']
- ),
- customxml.PubSubXMPPURIProperty(
- getPubSubXMPPURI(id, pubSubConfiguration)
- ),
- type="XMPP",
- )
- )
-
- propVal = customxml.PubSubPushTransportsProperty(*children)
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the value
- d.addBoth(lambda ignored: propVal)
- return d
-
-
- else:
- return succeed(customxml.PubSubPushTransportsProperty())
-
- if qname == (customxml.calendarserver_namespace, "pushkey"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- if getattr(self, "clientNotifier", None) is not None:
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- propVal = customxml.PubSubXMPPPushKeyProperty(nodeName)
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the xmpp-uri value
- d.addBoth(lambda ignored: propVal)
- return d
- else:
- return succeed(customxml.PubSubXMPPPushKeyProperty())
-
-
- if qname == (customxml.calendarserver_namespace, "xmpp-uri"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- if getattr(self, "clientNotifier", None) is not None:
- id = self.clientNotifier.getID()
- nodeName = getPubSubPath(id, pubSubConfiguration)
- propVal = customxml.PubSubXMPPURIProperty(
- getPubSubXMPPURI(id, pubSubConfiguration))
- nodeCacher = getNodeCacher()
- d = nodeCacher.waitForNode(self.clientNotifier, nodeName)
- # In either case we're going to return the xmpp-uri value
- d.addBoth(lambda ignored: propVal)
- return d
- else:
- return succeed(customxml.PubSubXMPPURIProperty())
-
- elif qname == (customxml.calendarserver_namespace, "xmpp-heartbeat-uri"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- return succeed(
- customxml.PubSubHeartbeatProperty(
- customxml.PubSubHeartbeatURIProperty(
- getPubSubHeartbeatURI(pubSubConfiguration)
- ),
- customxml.PubSubHeartbeatMinutesProperty(
- str(pubSubConfiguration['heartrate'])
- )
- )
- )
- else:
- return succeed(customxml.PubSubHeartbeatURIProperty())
-
- elif qname == (customxml.calendarserver_namespace, "xmpp-server"):
- pubSubConfiguration = getPubSubConfiguration(config)
- if pubSubConfiguration['enabled']:
- return succeed(customxml.PubSubXMPPServerProperty(
- pubSubConfiguration['xmpp-server']))
- else:
- return succeed(customxml.PubSubXMPPServerProperty())
-
- return super(AddressBookHomeFile, self).readProperty(property, request)
-
-
class DirectoryBackedAddressBookFile (ReadOnlyResourceMixIn, DirectoryBackedAddressBookResource, CalDAVFile):
"""
Directory-backed address book, supporting directory vcard search.
@@ -1756,23 +727,6 @@
from twistedcaldav.simpleresource import SimpleCalDAVResource
return SimpleCalDAVResource(principalCollections=self.principalCollections())
-class GlobalAddressBookFile (ReadOnlyResourceMixIn, GlobalAddressBookResource, CalDAVFile):
- """
- Directory-backed address book, supporting directory vcard search.
- """
- def __init__(self, path, principalCollections):
- CalDAVFile.__init__(self, path, principalCollections=principalCollections)
- self.clientNotifier = ClientNotifier(self)
-
- def createSimilarFile(self, path):
- if self.comparePath(path):
- return self
- else:
- similar = CalDAVFile(path, principalCollections=self.principalCollections())
- similar.clientNotifier = self.clientNotifier.clone(similar,
- label="collection")
- return similar
-
##
# Utilities
##
@@ -1790,43 +744,6 @@
# Otherwise, there is no child
return (None, ())
-def _schedulePrivilegeSet(deliver):
- 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 = list(supported_privilege.childrenOfType(davxml.SupportedPrivilege))
- all_supported_privileges.append(
- davxml.SupportedPrivilege(
- davxml.Privilege(caldavxml.ScheduleDeliver() if deliver else caldavxml.ScheduleSend()),
- davxml.Description("schedule privileges for current principal", **{"xml:lang": "en"}),
- ),
- )
- if config.Scheduling.CalDAV.OldDraftCompatibility:
- all_supported_privileges.append(
- davxml.SupportedPrivilege(
- davxml.Privilege(caldavxml.Schedule()),
- davxml.Description("old-style schedule privileges for current principal", **{"xml:lang": "en"}),
- ),
- )
- top_supported_privileges.append(
- davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
- )
- edited = True
- 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 schedulePrivilegeSet"
-
- return davxml.SupportedPrivilegeSet(*top_supported_privileges)
-
-deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
-sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
-
def _calendarPrivilegeSet ():
edited = False
@@ -1874,10 +791,6 @@
bindMethods(twistedcaldav.method, CalDAVFile)
-# Some resources do not support some methods
-setattr(CalendarHomeFile, "http_ACL", None)
-setattr(AddressBookHomeFile, "http_ACL", None)
-
# FIXME: Little bit of a circular dependency here...
twistedcaldav.method.acl.CalDAVFile = CalDAVFile
twistedcaldav.method.copymove.CalDAVFile = CalDAVFile
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/storebridge.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -14,6 +14,8 @@
# 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
@@ -45,17 +47,15 @@
from twext.web2.http import HTTPError, StatusResponse, Response
from twext.web2.stream import ProducerStream, readStream, MemoryStream
-from twistedcaldav.static import CalDAVFile, ScheduleInboxFile, \
- GlobalAddressBookFile
+from twistedcaldav.static import CalDAVFile
from twistedcaldav.vcard import Component as VCard
-from twistedcaldav.resource import CalDAVResource
+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.method.propfind import http_PROPFIND
from twistedcaldav.notifications import NotificationCollectionResource,\
NotificationResource
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
@@ -205,32 +205,28 @@
@classmethod
def transform(cls, self, calendar, home):
"""
- Transform C{self} into a L{CalendarCollectionFile}.
+ Transform C{self} into a L{CalendarCollectionResource}.
"""
self.__class__ = cls
self._initializeWithCalendar(calendar, home)
- def createSimilarFile(self, path):
+ def makeChild(self, name):
"""
Create a L{CalendarObjectFile} or L{ProtoCalendarObjectFile} based on a
path object.
"""
- if not isinstance(path, FilePath):
- path = FilePath(path)
- newStoreObject = self._newStoreCalendar.calendarObjectWithName(
- path.basename()
- )
+ newStoreObject = self._newStoreCalendar.calendarObjectWithName(name)
if newStoreObject is not None:
- similar = CalendarObjectFile(newStoreObject, path,
+ similar = CalendarObjectFile(newStoreObject, newStoreObject._path,
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, path,
+ similar = ProtoCalendarObjectFile(self._newStoreCalendar, self._newStoreCalendar._path.child(name),
principalCollections=self._principalCollections)
# FIXME: tests should be failing without this line.
@@ -238,17 +234,25 @@
self.propagateTransaction(similar)
return similar
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ children = set(self.putChildren.keys())
+ children.update(self._newStoreCalendar.listCalendarObjects())
+ return children
+
def quotaSize(self, request):
# FIXME: tests, workingness
return succeed(0)
-class StoreScheduleInboxFile(_CalendarChildHelper, ScheduleInboxFile):
+class StoreScheduleInboxResource(_CalendarChildHelper, ScheduleInboxResource):
def __init__(self, *a, **kw):
- super(StoreScheduleInboxFile, self).__init__(*a, **kw)
+ super(StoreScheduleInboxResource, self).__init__(*a, **kw)
self.parent.propagateTransaction(self)
home = self.parent._newStoreCalendarHome
storage = home.calendarWithName("inbox")
@@ -265,10 +269,6 @@
)
- def isCollection(self):
- return True
-
-
def provisionFile(self):
pass
@@ -297,7 +297,7 @@
qname = property.qname()
if qname == (dav_namespace, "resourcetype"):
- return self.resourceType(request)
+ return succeed(self.resourceType())
return super(_GetChildHelper, self).readProperty(property, request)
@@ -310,10 +310,7 @@
return super(_GetChildHelper, self).http_GET(request)
- http_PROPFIND = http_PROPFIND
-
-
class DropboxCollection(_GetChildHelper):
"""
A collection of all dropboxes (containers for attachments), presented as a
@@ -322,9 +319,7 @@
"""
# FIXME: no direct tests for this class at all.
- def __init__(self, path, parent, *a, **kw):
- # FIXME: constructor signature takes a 'path' because CalendarHomeFile
- # requires it, but we don't need it (and shouldn't have it) eventually.
+ def __init__(self, parent, *a, **kw):
super(DropboxCollection, self).__init__(
*a, principalCollections=parent.principalCollections(), **kw)
self._newStoreCalendarHome = parent._newStoreCalendarHome
@@ -349,8 +344,8 @@
return objectDropbox
- def resourceType(self, request):
- return succeed(davxml.ResourceType.dropboxhome)
+ def resourceType(self,):
+ return davxml.ResourceType.dropboxhome
def listChildren(self):
@@ -396,8 +391,8 @@
return True
- def resourceType(self, request):
- return succeed(davxml.ResourceType.dropbox)
+ def resourceType(self):
+ return davxml.ResourceType.dropbox
def getChild(self, name):
@@ -608,17 +603,18 @@
-class CalendarCollectionFile(_CalendarChildHelper, CalDAVFile):
+class CalendarCollectionResource(_CalendarChildHelper, CalDAVResource, DAVResourceWithChildrenMixin):
"""
Wrapper around a L{txcaldav.icalendar.ICalendar}.
"""
def __init__(self, calendar, home, *args, **kw):
"""
- Create a CalendarCollectionFile from a L{txcaldav.icalendar.ICalendar}
- and the arguments required for L{CalDAVFile}.
+ Create a CalendarCollectionResource from a L{txcaldav.icalendar.ICalendar}
+ and the arguments required for L{CalDAVResource}.
"""
- super(CalendarCollectionFile, self).__init__(*args, **kw)
+ super(CalendarCollectionResource, self).__init__(*args, **kw)
+ DAVResourceWithChildrenMixin.__init__(self)
self._initializeWithCalendar(calendar, home)
@@ -690,7 +686,7 @@
# Is this a sharee's view of a shared calendar? If so, they can't do
# scheduling onto it, so just delete it and move on.
- isVirtual = yield self.isVirtualShare(request)
+ isVirtual =self.isVirtualShare()
if isVirtual:
log.debug("Removing shared calendar %s" % (self,))
yield self.removeVirtualShare(request)
@@ -729,7 +725,7 @@
self._newStoreParentHome.removeCalendarWithName(
self._newStoreCalendar.name()
)
- self.__class__ = ProtoCalendarCollectionFile
+ self.__class__ = ProtoCalendarCollectionResource
del self._newStoreCalendar
# FIXME: handle exceptions, possibly like this:
@@ -773,17 +769,17 @@
basename = destination.fp.basename()
calendar = self._newStoreCalendar
calendar.rename(basename)
- CalendarCollectionFile.transform(destination, calendar,
+ CalendarCollectionResource.transform(destination, calendar,
self._newStoreParentHome)
del self._newStoreCalendar
- self.__class__ = ProtoCalendarCollectionFile
+ self.__class__ = ProtoCalendarCollectionResource
self.movedCalendar(request, defaultCalendar,
destination, destinationURI)
returnValue(NO_CONTENT)
-class NoParent(CalDAVFile):
+class NoParent(CalDAVResource):
def http_MKCALENDAR(self, request):
return CONFLICT
@@ -793,7 +789,7 @@
-class ProtoCalendarCollectionFile(CalDAVFile):
+class ProtoCalendarCollectionResource(CalDAVResource):
"""
A resource representing a calendar collection which hasn't yet been created.
"""
@@ -801,7 +797,7 @@
def __init__(self, home, *args, **kw):
"""
A placeholder resource for a calendar collection which does not yet
- exist, but will become a L{CalendarCollectionFile}.
+ exist, but will become a L{CalendarCollectionResource}.
@param home: The calendar home which will be this resource's parent,
when it exists.
@@ -809,18 +805,18 @@
@type home: L{txcaldav.icalendarstore.ICalendarHome}
"""
self._newStoreParentHome = home
- super(ProtoCalendarCollectionFile, self).__init__(*args, **kw)
+ super(ProtoCalendarCollectionResource, self).__init__(*args, **kw)
def isCollection(self):
return True
- def createSimilarFile(self, path):
+ def makeChild(self, name):
# FIXME: this is necessary for
# twistedcaldav.test.test_mkcalendar.
# MKCALENDAR.test_make_calendar_no_parent - there should be a more
# structured way to refuse creation with a non-existent parent.
- return NoParent(path)
+ return NoParent()
def provisionFile(self):
@@ -842,7 +838,7 @@
newStoreCalendar = self._newStoreParentHome.calendarWithName(
calendarName
)
- CalendarCollectionFile.transform(
+ CalendarCollectionResource.transform(
self, newStoreCalendar, self._newStoreParentHome
)
return d
@@ -1008,7 +1004,7 @@
required.
@param request: Unused by this implementation; present for signature
- compatibility with L{CalendarCollectionFile.storeRemove}.
+ compatibility with L{CalendarCollectionResource.storeRemove}.
@type request: L{twext.web2.iweb.IRequest}
@@ -1194,32 +1190,27 @@
@classmethod
def transform(cls, self, addressbook, home):
"""
- Transform C{self} into a L{AddressBookCollectionFile}.
+ Transform C{self} into a L{AddressBookCollectionResource}.
"""
self.__class__ = cls
self._initializeWithAddressBook(addressbook, home)
- def createSimilarFile(self, path):
+ def makeChild(self, name):
"""
Create a L{AddressBookObjectFile} or L{ProtoAddressBookObjectFile} based on a
path object.
"""
- if not isinstance(path, FilePath):
- path = FilePath(path)
+ newStoreObject = self._newStoreAddressBook.addressbookObjectWithName(name)
- newStoreObject = self._newStoreAddressBook.addressbookObjectWithName(
- path.basename()
- )
-
if newStoreObject is not None:
- similar = AddressBookObjectFile(newStoreObject, path,
+ similar = AddressBookObjectFile(newStoreObject, newStoreObject._path,
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, path,
+ similar = ProtoAddressBookObjectFile(self._newStoreAddressBook, self._newStoreAddressBook._path.child(name),
principalCollections=self._principalCollections)
# FIXME: tests should be failing without this line.
@@ -1227,24 +1218,34 @@
self.propagateTransaction(similar)
return similar
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ children = set(self.putChildren.keys())
+ children.update(self._newStoreAddressBook.listAddressbookObjects())
+ return children
+
+
def quotaSize(self, request):
# FIXME: tests, workingness
return succeed(0)
-class AddressBookCollectionFile(_AddressBookChildHelper, CalDAVFile):
+class AddressBookCollectionResource(_AddressBookChildHelper, CalDAVResource, DAVResourceWithChildrenMixin):
"""
Wrapper around a L{txcarddav.iaddressbook.IAddressBook}.
"""
def __init__(self, addressbook, home, *args, **kw):
"""
- Create a AddressBookCollectionFile from a L{txcarddav.iaddressbook.IAddressBook}
- and the arguments required for L{CalDAVFile}.
+ Create a AddressBookCollectionResource from a L{txcarddav.iaddressbook.IAddressBook}
+ and the arguments required for L{CalDAVResource}.
"""
- super(AddressBookCollectionFile, self).__init__(*args, **kw)
+ super(AddressBookCollectionResource, self).__init__(*args, **kw)
+ DAVResourceWithChildrenMixin.__init__(self)
self._initializeWithAddressBook(addressbook, home)
@@ -1301,7 +1302,7 @@
"""
# Check virtual share first
- isVirtual = yield self.isVirtualShare(request)
+ isVirtual = self.isVirtualShare()
if isVirtual:
log.debug("Removing shared calendar %s" % (self,))
yield self.removeVirtualShare(request)
@@ -1340,7 +1341,7 @@
self._newStoreParentHome.removeAddressBookWithName(
self._newStoreAddressBook.name()
)
- self.__class__ = ProtoAddressBookCollectionFile
+ self.__class__ = ProtoAddressBookCollectionResource
del self._newStoreAddressBook
# FIXME: handle exceptions, possibly like this:
@@ -1379,15 +1380,15 @@
basename = destination.fp.basename()
addressbook = self._newStoreAddressBook
addressbook.rename(basename)
- AddressBookCollectionFile.transform(destination, addressbook,
+ AddressBookCollectionResource.transform(destination, addressbook,
self._newStoreParentHome)
del self._newStoreAddressBook
- self.__class__ = ProtoAddressBookCollectionFile
+ self.__class__ = ProtoAddressBookCollectionResource
returnValue(NO_CONTENT)
-class ProtoAddressBookCollectionFile(CalDAVFile):
+class ProtoAddressBookCollectionResource(CalDAVResource):
"""
A resource representing an addressbook collection which hasn't yet been created.
"""
@@ -1395,7 +1396,7 @@
def __init__(self, home, *args, **kw):
"""
A placeholder resource for an addressbook collection which does not yet
- exist, but will become a L{AddressBookCollectionFile}.
+ exist, but will become a L{AddressBookCollectionResource}.
@param home: The addressbook home which will be this resource's parent,
when it exists.
@@ -1403,19 +1404,19 @@
@type home: L{txcarddav.iaddressbookstore.IAddressBookHome}
"""
self._newStoreParentHome = home
- super(ProtoAddressBookCollectionFile, self).__init__(*args, **kw)
+ super(ProtoAddressBookCollectionResource, self).__init__(*args, **kw)
def isCollection(self):
return True
- def createSimilarFile(self, path):
+ def makeChild(self, name):
# FIXME: this is necessary for
# twistedcaldav.test.test_mkcol.
# MKCOL.test_make_addressbook_no_parent - there should be a more
# structured way to refuse creation with a non-existent parent.
- return NoParent(path)
+ return NoParent()
def provisionFile(self):
@@ -1438,7 +1439,7 @@
newStoreAddressBook = self._newStoreParentHome.addressbookWithName(
Name
)
- AddressBookCollectionFile.transform(
+ AddressBookCollectionResource.transform(
self, newStoreAddressBook, self._newStoreParentHome
)
return d
@@ -1461,104 +1462,19 @@
return succeed(0)
-class GlobalAddressBookCollectionFile(_AddressBookChildHelper, GlobalAddressBookFile):
+class GlobalAddressBookCollectionResource(GlobalAddressBookResource, AddressBookCollectionResource):
"""
Wrapper around a L{txcarddav.iaddressbook.IAddressBook}.
"""
+ pass
- def __init__(self, addressbook, home, *args, **kw):
- """
- Create a GlobalAddressBookCollectionFile from a L{txcarddav.iaddressbook.IAddressBook}
- and the arguments required for L{CalDAVFile}.
- """
- super(GlobalAddressBookCollectionFile, self).__init__(*args, **kw)
- self._initializeWithAddressBook(addressbook, home)
-
-
- def isCollection(self):
- return True
-
- def isAddressBookCollection(self):
- """
- Yes, it is a calendar collection.
- """
- return True
-
-class ProtoGlobalAddressBookCollectionFile(GlobalAddressBookFile):
+class ProtoGlobalAddressBookCollectionResource(GlobalAddressBookResource, ProtoAddressBookCollectionResource):
"""
A resource representing an addressbook collection which hasn't yet been created.
"""
+ pass
- def __init__(self, home, *args, **kw):
- """
- A placeholder resource for an addressbook collection which does not yet
- exist, but will become a L{GlobalAddressBookCollectionFile}.
- @param home: The addressbook home which will be this resource's parent,
- when it exists.
-
- @type home: L{txcarddav.iaddressbookstore.IAddressBookHome}
- """
- self._newStoreParentHome = home
- super(ProtoGlobalAddressBookCollectionFile, self).__init__(*args, **kw)
-
-
- def isCollection(self):
- return True
-
-
- def createSimilarFile(self, path):
- # FIXME: this is necessary for
- # twistedcaldav.test.test_mkcol.
- # MKCOL.test_make_addressbook_no_parent - there should be a more
- # structured way to refuse creation with a non-existent parent.
- return NoParent(path)
-
-
- def provisionFile(self):
- """
- Create an addressbook collection.
- """
- # FIXME: this should be done in the backend; provisionDefaultAddressBooks
- # should go away.
- return self.createAddressBookCollection()
-
-
- def createAddressBookCollection(self):
- """
- Override C{createAddressBookCollection} to actually do the work.
- """
- d = succeed(CREATED)
-
- Name = self.fp.basename()
- self._newStoreParentHome.createAddressBookWithName(Name)
- newStoreAddressBook = self._newStoreParentHome.addressbookWithName(
- Name
- )
- GlobalAddressBookCollectionFile.transform(
- self, newStoreAddressBook, self._newStoreParentHome
- )
- return d
-
-
- def exists(self):
- # FIXME: tests
- return False
-
-
- def provision(self):
- """
- This resource should do nothing if it's provisioned.
- """
- # FIXME: should be deleted, or raise an exception
-
-
- def quotaSize(self, request):
- # FIXME: tests, workingness
- return succeed(0)
-
-
-
class AddressBookObjectFile(CalDAVFile, FancyEqMixin):
"""
A resource wrapping a addressbook object.
@@ -1785,9 +1701,6 @@
return self.getChild(segments[0]), segments[1:]
- def getChild(self, name):
- return None
-
def notificationsDB(self):
"""
Retrieve the new-style index wrapper.
@@ -1809,18 +1722,13 @@
self._initializeWithNotifications(notifications, home)
- def createSimilarFile(self, path):
+ def makeChild(self, name):
"""
Create a L{NotificationObjectFile} or L{ProtoNotificationObjectFile} based on a
path object.
"""
- if not isinstance(path, FilePath):
- path = FilePath(path)
+ newStoreObject = self._newStoreNotifications.notificationObjectWithName(name)
- newStoreObject = self._newStoreNotifications.notificationObjectWithName(
- path.basename()
- )
-
if newStoreObject is not None:
similar = StoreNotificationObjectFile(newStoreObject, self)
else:
@@ -1834,14 +1742,23 @@
self.propagateTransaction(similar)
return similar
+ def listChildren(self):
+ """
+ @return: a sequence of the names of all known children of this resource.
+ """
+ children = set(self.putChildren.keys())
+ children.update(self._newStoreNotifications.listNotificationObjects())
+ return children
+
+
def quotaSize(self, request):
# FIXME: tests, workingness
return succeed(0)
-class NotificationCollectionFile(_NotificationChildHelper,
+class StoreNotificationCollectionResource(_NotificationChildHelper,
NotificationCollectionResource):
"""
Wrapper around a L{txcaldav.icalendar.ICalendar}.
@@ -1849,25 +1766,13 @@
def __init__(self, notifications, home, *args, **kw):
"""
- Create a CalendarCollectionFile from a L{txcaldav.icalendar.ICalendar}
- and the arguments required for L{CalDAVFile}.
+ Create a CalendarCollectionResource from a L{txcaldav.icalendar.ICalendar}
+ and the arguments required for L{CalDAVResource}.
"""
- super(NotificationCollectionFile, self).__init__(*args, **kw)
+ super(StoreNotificationCollectionResource, self).__init__(*args, **kw)
self._initializeWithNotifications(notifications, home)
- def getChild(self, name):
- notificationObject = self._newStoreNotifications.notificationObjectWithName(name)
- if notificationObject is None:
- return None
- notification = StoreNotificationObjectFile(
- notificationObject,
- self,
- )
- self.propagateTransaction(notification)
- return notification
-
-
def listChildren(self):
l = []
for notification in self._newStoreNotifications.notificationObjects():
@@ -1886,7 +1791,7 @@
self._newStoreNotifications.removeNotificationObjectWithName(record.name)
return succeed(None)
-class ProtoNotificationCollectionFile(NotificationCollectionResource):
+class StoreProtoNotificationCollectionResource(NotificationCollectionResource):
"""
A resource representing a notification collection which hasn't yet been created.
"""
@@ -1894,7 +1799,7 @@
def __init__(self, home, *args, **kw):
"""
A placeholder resource for a notification collection which does not yet
- exist, but will become a L{NotificationCollectionFile}.
+ exist, but will become a L{StoreNotificationCollectionResource}.
@param home: The calendar home which will be this resource's parent,
when it exists.
@@ -1902,18 +1807,18 @@
@type home: L{txcaldav.icalendarstore.ICalendarHome}
"""
self._newStoreParentHome = home
- super(ProtoNotificationCollectionFile, self).__init__(*args, **kw)
+ super(StoreProtoNotificationCollectionResource, self).__init__(*args, **kw)
def isCollection(self):
return True
- def createSimilarFile(self, path):
+ def makeChild(self, name):
# FIXME: this is necessary for
# twistedcaldav.test.test_mkcalendar.
# MKCALENDAR.test_make_calendar_no_parent - there should be a more
# structured way to refuse creation with a non-existent parent.
- return NoParent(path)
+ return NoParent()
def provisionFile(self):
@@ -1935,7 +1840,7 @@
newStoreNotification = self._newStoreParentHome.childWithName(
notificationName
)
- NotificationCollectionFile.transform(
+ StoreNotificationCollectionResource.transform(
self, newStoreNotification, self._newStoreParentHome
)
return d
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_schedule.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_schedule.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_schedule.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -14,8 +14,6 @@
# limitations under the License.
##
-import os
-
from twext.web2 import responsecode
from twext.web2.iweb import IResponse
from twext.web2.dav import davxml
@@ -24,7 +22,6 @@
from twext.web2.test.test_server import SimpleRequest
from twistedcaldav import caldavxml
-from twistedcaldav.static import ScheduleInboxFile
from twistedcaldav.test.util import HomeTestCase
@@ -38,8 +35,6 @@
"""
inbox_uri = "/inbox/"
- #inbox_path = os.path.join(self.docroot, "inbox")
- #self.site.resource.putChild("inbox", ScheduleInboxFile(inbox_path, self.site.resource))
def propfind_cb(response):
response = IResponse(response)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_sharing.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_sharing.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -103,42 +103,42 @@
def test_upgradeToShareOnCreate(self):
request = SimpleRequest(self.site, "MKCOL", "/calendar/")
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.calendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, request))
self.assertEquals(propInvite, None)
- yield self.resource.upgradeToShare(request)
+ self.resource.upgradeToShare()
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.sharedownercalendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, request))
self.assertEquals(propInvite, customxml.Invite())
isShared = (yield self.resource.isShared(request))
self.assertTrue(isShared)
- isVShared = (yield self.resource.isVirtualShare(request))
+ isVShared = self.resource.isVirtualShare()
self.assertFalse(isVShared)
@inlineCallbacks
def test_upgradeToShareAfterCreate(self):
request = SimpleRequest(self.site, "PROPPATCH", "/calendar/")
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.calendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, request))
self.assertEquals(propInvite, None)
- yield self.resource.upgradeToShare(request)
+ self.resource.upgradeToShare()
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.sharedownercalendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, request))
self.assertEquals(propInvite, customxml.Invite())
isShared = (yield self.resource.isShared(request))
self.assertTrue(isShared)
- isVShared = (yield self.resource.isVirtualShare(request))
+ isVShared = self.resource.isVirtualShare()
self.assertFalse(isVShared)
@inlineCallbacks
@@ -147,27 +147,27 @@
self.resource.writeDeadProperty(davxml.ResourceType.sharedownercalendar)
self.resource.writeDeadProperty(customxml.Invite())
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.sharedownercalendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, None))
self.assertEquals(propInvite, customxml.Invite())
yield self.resource.downgradeFromShare(None)
- rtype = (yield self.resource.resourceType(request))
+ rtype = self.resource.resourceType()
self.assertEquals(rtype, davxml.ResourceType.calendar)
propInvite = (yield self.resource.readProperty(customxml.Invite, None))
self.assertEquals(propInvite, None)
isShared = (yield self.resource.isShared(None))
self.assertFalse(isShared)
- isVShared = (yield self.resource.isVirtualShare(None))
+ isVShared = self.resource.isVirtualShare()
self.assertFalse(isVShared)
@inlineCallbacks
def test_POSTaddInviteeAlreadyShared(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -192,7 +192,7 @@
isShared = (yield self.resource.isShared(None))
self.assertTrue(isShared)
- isVShared = (yield self.resource.isVirtualShare(None))
+ isVShared = self.resource.isVirtualShare()
self.assertFalse(isVShared)
@inlineCallbacks
@@ -228,7 +228,7 @@
@inlineCallbacks
def test_POSTupdateInvitee(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -264,7 +264,7 @@
@inlineCallbacks
def test_POSTremoveInvitee(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -290,7 +290,7 @@
@inlineCallbacks
def test_POSTaddMoreInvitees(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -344,7 +344,7 @@
@inlineCallbacks
def test_POSTaddRemoveInvitees(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -394,7 +394,7 @@
@inlineCallbacks
def test_POSTaddRemoveSameInvitee(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -444,7 +444,7 @@
@inlineCallbacks
def test_POSTaddInvalidInvitee(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
response = (yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
@@ -475,7 +475,7 @@
@inlineCallbacks
def test_POSTremoveInvalidInvitee(self):
- yield self.resource.upgradeToShare(SimpleRequest(self.site, "MKCOL", "/calendar/"))
+ self.resource.upgradeToShare()
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_wrapping.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/test_wrapping.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -27,8 +27,8 @@
from twistedcaldav.ical import Component as VComponent
from twistedcaldav.vcard import Component as VCComponent
-from twistedcaldav.storebridge import ProtoCalendarCollectionFile, \
- ProtoAddressBookCollectionFile, DropboxCollection
+from twistedcaldav.storebridge import ProtoCalendarCollectionResource, \
+ ProtoAddressBookCollectionResource, DropboxCollection
from twistedcaldav.test.util import TestCase
@@ -137,7 +137,7 @@
def test_createStore(self):
"""
- Creating a CalendarHomeProvisioningFile will create a paired
+ Creating a DirectoryCalendarHomeProvisioningResource will create a paired
CalendarStore.
"""
self.assertIsInstance(self.calendarCollection._newStore, CalendarStore)
@@ -149,7 +149,7 @@
def test_lookupCalendarHome(self):
"""
When a L{CalDAVFile} representing an existing calendar home is looked
- up in a CalendarHomeFile, it will create a corresponding
+ up in a CalendarHomeResource, it will create a corresponding
L{CalendarHome} via C{newTransaction().calendarHomeWithUID}.
"""
calDavFile = yield self.getResource("calendars/users/wsanchez/")
@@ -171,7 +171,7 @@
)
self.commit()
self.assertIsInstance(dropBoxResource, DropboxCollection)
- self.assertEquals((yield dropBoxResource.resourceType(None)),
+ self.assertEquals(dropBoxResource.resourceType(),
davxml.ResourceType.dropboxhome)
@@ -179,14 +179,14 @@
def test_lookupExistingCalendar(self):
"""
When a L{CalDAVFile} representing an existing calendar collection is
- looked up in a L{CalendarHomeFile} representing a calendar home, it
+ looked up in a L{CalendarHomeResource} representing a calendar home, it
will create a corresponding L{Calendar} via
C{CalendarHome.calendarWithName}.
"""
calDavFile = yield self.getResource("calendars/users/wsanchez/calendar")
self.commit()
self.assertEquals(calDavFile.fp, calDavFile._newStoreCalendar._path)
- self.assertEquals((yield calDavFile.resourceType(None)),
+ self.assertEquals(calDavFile.resourceType(),
davxml.ResourceType.calendar)
@@ -194,13 +194,13 @@
def test_lookupNewCalendar(self):
"""
When a L{CalDAVFile} which represents a not-yet-created calendar
- collection is looked up in a L{CalendarHomeFile} representing a calendar
+ collection is looked up in a L{CalendarHomeResource} representing a calendar
home, it will initially have a new storage backend set to C{None}, but
when the calendar is created via a protocol action, the backend will be
initialized to match.
"""
calDavFile = yield self.getResource("calendars/users/wsanchez/frobozz")
- self.assertIsInstance(calDavFile, ProtoCalendarCollectionFile)
+ self.assertIsInstance(calDavFile, ProtoCalendarCollectionResource)
calDavFile.createCalendarCollection()
self.commit()
self.assertEquals(calDavFile.fp, calDavFile._newStoreCalendar._path)
@@ -211,7 +211,7 @@
"""
When a L{CalDAVFile} I{not} representing a calendar collection - one of
the special collections, like the dropbox or freebusy URLs - is looked
- up in a L{CalendarHomeFile} representing a calendar home, it will I{not}
+ up in a L{CalendarHomeResource} representing a calendar home, it will I{not}
create a corresponding L{Calendar} via C{CalendarHome.calendarWithName}.
"""
for specialName in ['dropbox', 'freebusy', 'notifications']:
@@ -301,7 +301,7 @@
initialized to match.
"""
calDavFile = yield self.getResource("addressbooks/users/wsanchez/frobozz")
- self.assertIsInstance(calDavFile, ProtoAddressBookCollectionFile)
+ self.assertIsInstance(calDavFile, ProtoAddressBookCollectionResource)
calDavFile.createAddressBookCollection()
self.commit()
self.assertEquals(calDavFile.fp, calDavFile._newStoreAddressBook._path)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/util.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/test/util.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -40,12 +40,12 @@
from twistedcaldav import memcacher
from twistedcaldav.config import config
-from twistedcaldav.static import CalDAVFile, CalendarHomeProvisioningFile,\
- AddressBookHomeProvisioningFile
-from twistedcaldav.directory.xmlfile import XMLDirectoryService
+from twistedcaldav.static import CalDAVFile, AddressBookHomeProvisioningFile
from twistedcaldav.directory import augment
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
from twistedcaldav.directory.principal import (
DirectoryPrincipalProvisioningResource)
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
from txdav.common.datastore.file import CommonDataStore
@@ -96,7 +96,7 @@
def setupCalendars(self):
"""
- Set up the resource at /calendars (a L{CalendarHomeProvisioningFile}),
+ Set up the resource at /calendars (a L{DirectoryCalendarHomeProvisioningResource}),
and assign it as C{self.calendarCollection}.
"""
path = self.site.resource.fp.child("calendars")
@@ -105,8 +105,7 @@
# Need a data store
_newStore = CommonDataStore(self.site.resource.fp, True, False)
- self.calendarCollection = CalendarHomeProvisioningFile(
- path,
+ self.calendarCollection = DirectoryCalendarHomeProvisioningResource(
self.directoryService,
"/calendars/",
_newStore
@@ -293,7 +292,7 @@
def setUp(self):
"""
Replace self.site.resource with an appropriately provisioned
- CalendarHomeFile, and replace self.docroot with a path pointing at that
+ CalendarHomeResource, and replace self.docroot with a path pointing at that
file.
"""
super(HomeTestCase, self).setUp()
@@ -305,8 +304,7 @@
# Need a data store
_newStore = CommonDataStore(fp, True, False)
- self.homeProvisioner = CalendarHomeProvisioningFile(
- os.path.join(fp.path, "calendars"),
+ self.homeProvisioner = DirectoryCalendarHomeProvisioningResource(
self.directoryService, "/calendars/",
_newStore
)
Modified: CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/timezoneservice.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/timezoneservice.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/twistedcaldav/timezoneservice.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -90,8 +90,8 @@
),
)
- def resourceType(self, request):
- return succeed(davxml.ResourceType.timezones)
+ def resourceType(self):
+ return davxml.ResourceType.timezones
def isCollection(self):
return False
Modified: CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/txcaldav/calendarstore/file.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -75,7 +75,7 @@
def calendarWithName(self, name):
- if name == 'dropbox':
+ if name in ('dropbox', 'notifications', 'freebusy'):
# "dropbox" is a file storage area, not a calendar.
return None
else:
@@ -86,12 +86,24 @@
removeCalendarWithName = CommonHome.removeChildWithName
def calendars(self):
+ """
+ Return a generator of the child resource objects.
+ """
for child in self.children():
if child.name() in ('dropbox', 'notification'):
continue
yield child
+ def listCalendars(self):
+ """
+ Return a generator of the child resource names.
+ """
+ for name in self.listChildren():
+ if name in ('dropbox', 'notification'):
+ continue
+ yield name
+
def calendarObjectWithDropboxID(self, dropboxID):
"""
Implement lookup with brute-force scanning.
@@ -157,6 +169,7 @@
ownerCalendarHome = CommonHomeChild.ownerHome
calendarObjects = CommonHomeChild.objectResources
+ listCalendarObjects = CommonHomeChild.listObjectResources
calendarObjectWithName = CommonHomeChild.objectResourceWithName
calendarObjectWithUID = CommonHomeChild.objectResourceWithUID
createCalendarObjectWithName = CommonHomeChild.createObjectResourceWithName
Modified: CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/txcarddav/addressbookstore/file.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -64,6 +64,7 @@
self._childClass = AddressBook
addressbooks = CommonHome.children
+ listAddressbooks = CommonHome.listChildren
addressbookWithName = CommonHome.childWithName
createAddressBookWithName = CommonHome.createChildWithName
removeAddressBookWithName = CommonHome.removeChildWithName
@@ -112,6 +113,7 @@
ownerAddressBookHome = CommonHomeChild.ownerHome
addressbookObjects = CommonHomeChild.objectResources
+ listAddressbookObjects = CommonHomeChild.listObjectResources
addressbookObjectWithName = CommonHomeChild.objectResourceWithName
addressbookObjectWithUID = CommonHomeChild.objectResourceWithUID
createAddressBookObjectWithName = CommonHomeChild.createObjectResourceWithName
Modified: CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/__init__.py
===================================================================
--- CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/__init__.py 2010-07-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/__init__.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 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.
+##
+
+"""
+xxxDAV protocol data store for Twisted.
+"""
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-20 02:56:47 UTC (rev 5922)
+++ CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/file.py 2010-07-21 20:29:32 UTC (rev 5923)
@@ -295,12 +295,27 @@
return self._shares
def children(self):
+ """
+ Return a set of the child resource objects.
+ """
return set(self._newChildren.itervalues()) | set(
self.childWithName(name)
for name in self._path.listdir()
if not name.startswith(".")
)
+ def listChildren(self):
+ """
+ Return a set of the names of the child resources.
+ """
+ return 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)
if child is not None:
@@ -508,6 +523,9 @@
self.properties().setPerUserUID(uid)
def objectResources(self):
+ """
+ Return a list of object resource objects.
+ """
return sorted((
self.objectResourceWithName(name)
for name in (
@@ -520,6 +538,21 @@
)
+ def listObjectResources(self):
+ """
+ Return a list of object resource names.
+ """
+ return sorted((
+ name
+ for name in (
+ set(self._newObjectResources.iterkeys()) |
+ set(name for name in self._path.listdir()
+ if not name.startswith(".")) -
+ set(self._removedObjectResources)
+ ))
+ )
+
+
def objectResourceWithName(self, name):
if name in self._removedObjectResources:
return None
@@ -739,6 +772,7 @@
return ResourceType.notification
notificationObjects = CommonHomeChild.objectResources
+ listNotificationObjects = CommonHomeChild.listObjectResources
notificationObjectWithName = CommonHomeChild.objectResourceWithName
removeNotificationObjectWithUID = CommonHomeChild.removeObjectResourceWithUID
notificationObjectsSinceToken = CommonHomeChild.objectResourcesSinceToken
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100721/d34c1d56/attachment-0001.html>
More information about the calendarserver-changes
mailing list