[CalendarServer-changes] [1295]
CalendarServer/branches/users/cdaboo/sqlprops-1202
source_changes at macosforge.org
source_changes at macosforge.org
Wed Feb 28 14:06:41 PST 2007
Revision: 1295
http://trac.macosforge.org/projects/calendarserver/changeset/1295
Author: cdaboo at apple.com
Date: 2007-02-28 14:06:40 -0800 (Wed, 28 Feb 2007)
Log Message:
-----------
SQL property database implementation that does a "batch" query on all child resources. Still a work in progress.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.element.base.patch
CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.resource.patch
CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/extensions.py
CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/method/put_common.py
CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/sqlprops.py
CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/static.py
CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/tap.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.element.base.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.element.base.patch 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.element.base.patch 2007-02-28 22:06:40 UTC (rev 1295)
@@ -2,6 +2,15 @@
===================================================================
--- twisted/web2/dav/element/base.py (revision 18545)
+++ twisted/web2/dav/element/base.py (working copy)
+@@ -45,7 +45,7 @@
+ ]
+
+ import string
+-import StringIO
++import cStringIO
+ import xml.dom.minidom
+
+ import datetime
@@ -145,21 +145,20 @@
if self.allowed_attributes:
@@ -123,10 +132,11 @@
+ # Quote any single quotes. We do not need to be any smarter than this.
+ value = value.replace("'", "'")
+
-+ output.write(" %s='%s'" % (name, value,))
++ output.write(" %s='%s'" % (name, value,))
+
def toxml(self):
- output = StringIO.StringIO()
+- output = StringIO.StringIO()
++ output = cStringIO.StringIO()
self.writeXML(output)
- return output.getvalue()
+ return str(output.getvalue())
@@ -147,7 +157,7 @@
+ if "&" in cdata:
+ cdata = cdata.replace("&", "&")
+ if "<" in cdata:
-+ cdata = cdata.replace("&", "<")
++ cdata = cdata.replace("<", "<")
+ if "]]>" in cdata:
+ cdata = cdata.replace("]]>", "]]<")
+
Added: CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch (rev 0)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch 2007-02-28 22:06:40 UTC (rev 1295)
@@ -0,0 +1,280 @@
+Index: twisted/web2/dav/method/propfind.py
+===================================================================
+--- twisted/web2/dav/method/propfind.py (revision 18545)
++++ twisted/web2/dav/method/propfind.py (working copy)
+@@ -22,6 +22,8 @@
+ #
+ # DRI: Wilfredo Sanchez, wsanchez at apple.com
+ ##
++from twisted.web2.dav.util import joinURL
++import urllib
+
+ """
+ WebDAV PROPFIND method
+@@ -114,79 +116,205 @@
+ yield filtered_aces
+ filtered_aces = filtered_aces.getResult()
+
+- resources = [(self, my_url)]
++ resources = [my_url.rstrip("/")]
+
+- d = self.findChildren(depth, request, lambda x, y: resources.append((x, y)), (davxml.Read(),), inherited_aces=filtered_aces)
+- x = waitForDeferred(d)
+- yield x
+- x.getResult()
++# d = self.findChildrenBatch(depth, request, lambda x, y: resources.append((x, y)), (davxml.Read(),), inherited_aces=filtered_aces)
++# x = waitForDeferred(d)
++# yield x
++# x.getResult()
++ if depth == "1":
++ d = self.findChildNames(request, lambda x: resources.append(joinURL(request.uri, x)), privileges=(davxml.Read(),), inherited_aces=filtered_aces)
++ x = waitForDeferred(d)
++ yield x
++ x.getResult()
+
+- for resource, uri in resources:
+- if search_properties is "names":
+- try:
+- resource_properties = waitForDeferred(resource.listProperties(request))
+- yield resource_properties
+- resource_properties = resource_properties.getResult()
+- except:
+- log.err("Unable to get properties for resource %r" % (resource,))
+- raise
++ if search_properties is "names":
++ results = {}
++ results[request.uri] = self.deadProperties().list()
++
++ if depth == "1":
++ allowed_uris = set(resources)
++ child_results = self.deadProperties().listAll()
++ for key, value in child_results.iteritems():
++ uri = joinURL(request.uri, key)
++ if uri in allowed_uris:
++ results[uri] = value
+
++ uris = results.keys()
++ uris.sort()
++ for uri in uris:
+ properties_by_status = {
+- responsecode.OK: [propertyName(p) for p in resource_properties]
++ responsecode.OK: [propertyName(p) for p in results[uri]]
+ }
+- else:
++ propstats = []
++
++ for status in properties_by_status:
++ properties = properties_by_status[status]
++ if not properties: continue
++
++ xml_status = davxml.Status.fromResponseCode(status)
++ xml_container = davxml.PropertyContainer(*properties)
++ xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
++
++ propstats.append(xml_propstat)
++
++ xml_resource = davxml.HRef(uri)
++ if propstats:
++ xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
++ else:
++ xml_response = davxml.StatusResponse(xml_resource, davxml.Status.fromResponseCode(responsecode.OK))
++
++ xml_responses.append(xml_response)
++
++ elif search_properties is "all":
++ results = {}
++ results[request.uri] = self.deadProperties().getAll(nohidden=True)
++
++ if depth == "1":
++ allowed_uris = set(resources)
++ child_results = self.deadProperties().getAllResources(nohidden=True)
++ for key, value in child_results.iteritems():
++ uri = joinURL(request.uri, key)
++ if uri in allowed_uris:
++ results[uri] = value
++
++ uris = results.keys()
++ uris.sort()
++ for uri in uris:
+ properties_by_status = {
++ responsecode.OK: [p for p in results[uri].itervalues()]
++ }
++ propstats = []
++
++ for status in properties_by_status:
++ properties = properties_by_status[status]
++ if not properties: continue
++
++ xml_status = davxml.Status.fromResponseCode(status)
++ xml_container = davxml.PropertyContainer(*properties)
++ xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
++
++ propstats.append(xml_propstat)
++
++ xml_resource = davxml.HRef(uri)
++ if propstats:
++ xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
++ else:
++ xml_response = davxml.StatusResponse(xml_resource, davxml.Status.fromResponseCode(responsecode.OK))
++
++ xml_responses.append(xml_response)
++
++ else:
++ results = {}
++ results[request.uri] = self.deadProperties().getSeveral(search_properties)
++
++ if depth == "1":
++ allowed_uris = set(resources)
++ child_results = self.deadProperties().getSeveralResources(search_properties)
++ for key, value in child_results.iteritems():
++ uri = joinURL(request.uri, key)
++ if uri in allowed_uris:
++ results[uri] = value
++
++ uris = results.keys()
++ uris.sort()
++ for uri in uris:
++ properties_by_status = {
+ responsecode.OK : [],
+ responsecode.NOT_FOUND : [],
+ }
++ for prop in search_properties:
++ if results[uri].has_key(prop):
++ properties_by_status[responsecode.OK].append(results[uri][prop])
++ else:
++ properties_by_status[responsecode.NOT_FOUND].append(propertyName(prop))
+
+- if search_properties is "all":
+- properties_to_enumerate = waitForDeferred(resource.listAllprop(request))
+- yield properties_to_enumerate
+- properties_to_enumerate = properties_to_enumerate.getResult()
++ propstats = []
++
++ for status in properties_by_status:
++ properties = properties_by_status[status]
++ if not properties: continue
++
++ xml_status = davxml.Status.fromResponseCode(status)
++ xml_container = davxml.PropertyContainer(*properties)
++ xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
++
++ propstats.append(xml_propstat)
++
++ xml_resource = davxml.HRef(uri)
++ if propstats:
++ xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
+ else:
+- properties_to_enumerate = search_properties
++ xml_response = davxml.StatusResponse(xml_resource, davxml.Status.fromResponseCode(responsecode.OK))
++
++ xml_responses.append(xml_response)
++
++# for resource, uri in resources:
++# if search_properties is "names":
++# try:
++# resource_properties = waitForDeferred(resource.listProperties(request))
++# yield resource_properties
++# resource_properties = resource_properties.getResult()
++# except:
++# log.err("Unable to get properties for resource %r" % (resource,))
++# raise
++#
++# properties_by_status = {
++# responsecode.OK: [propertyName(p) for p in resource_properties]
++# }
++# else:
++# properties_by_status = {
++# responsecode.OK : [],
++# responsecode.NOT_FOUND : [],
++# }
++#
++# if search_properties is "all":
++# properties_to_enumerate = waitForDeferred(resource.listAllprop(request))
++# yield properties_to_enumerate
++# properties_to_enumerate = properties_to_enumerate.getResult()
++# else:
++# properties_to_enumerate = search_properties
++#
++# for property in properties_to_enumerate:
++# has = waitForDeferred(resource.hasProperty(property, request))
++# yield has
++# has = has.getResult()
++# if has:
++# try:
++# resource_property = waitForDeferred(resource.readProperty(property, request))
++# yield resource_property
++# resource_property = resource_property.getResult()
++# except:
++# f = Failure()
++#
++# log.err("Error reading property %r for resource %s: %s" % (property, uri, f.value))
++#
++# status = statusForFailure(f, "getting property: %s" % (property,))
++# if status not in properties_by_status:
++# properties_by_status[status] = []
++# properties_by_status[status].append(propertyName(property))
++# else:
++# properties_by_status[responsecode.OK].append(resource_property)
++# else:
++# properties_by_status[responsecode.NOT_FOUND].append(propertyName(property))
++#
++# propstats = []
++#
++# for status in properties_by_status:
++# properties = properties_by_status[status]
++# if not properties: continue
++#
++# xml_status = davxml.Status.fromResponseCode(status)
++# xml_container = davxml.PropertyContainer(*properties)
++# xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
++#
++# propstats.append(xml_propstat)
++#
++# xml_resource = davxml.HRef(uri)
++# xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
++#
++# xml_responses.append(xml_response)
+
+- for property in properties_to_enumerate:
+- has = waitForDeferred(resource.hasProperty(property, request))
+- yield has
+- has = has.getResult()
+- if has:
+- try:
+- resource_property = waitForDeferred(resource.readProperty(property, request))
+- yield resource_property
+- resource_property = resource_property.getResult()
+- except:
+- f = Failure()
+-
+- log.err("Error reading property %r for resource %s: %s" % (property, uri, f.value))
+-
+- status = statusForFailure(f, "getting property: %s" % (property,))
+- if status not in properties_by_status:
+- properties_by_status[status] = []
+- properties_by_status[status].append(propertyName(property))
+- else:
+- properties_by_status[responsecode.OK].append(resource_property)
+- else:
+- properties_by_status[responsecode.NOT_FOUND].append(propertyName(property))
+-
+- propstats = []
+-
+- for status in properties_by_status:
+- properties = properties_by_status[status]
+- if not properties: continue
+-
+- xml_status = davxml.Status.fromResponseCode(status)
+- xml_container = davxml.PropertyContainer(*properties)
+- xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
+-
+- propstats.append(xml_propstat)
+-
+- xml_resource = davxml.HRef(uri)
+- xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
+-
+- xml_responses.append(xml_response)
+-
+ #
+ # Return response
+ #
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.resource.patch 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/lib-patches/Twisted/twisted.web2.dav.resource.patch 2007-02-28 22:06:40 UTC (rev 1295)
@@ -81,7 +81,7 @@
def ifAllowed(privileges, callback):
def onError(failure):
-@@ -286,7 +307,33 @@
+@@ -286,14 +307,36 @@
d.addCallback(gotACL)
return d
return ifAllowed((davxml.ReadACL(),), callback)
@@ -114,8 +114,16 @@
+
elif namespace == twisted_dav_namespace:
if name == "resource-class":
- class ResourceClass (davxml.WebDAVTextElement):
-@@ -363,15 +410,28 @@
+- class ResourceClass (davxml.WebDAVTextElement):
+- namespace = twisted_dav_namespace
+- name = "resource-class"
+- hidden = False
+- return ResourceClass(self.__class__.__name__)
++ return TwistedResourceClass(self.__class__.__name__)
+
+ elif namespace == twisted_private_namespace:
+ raise HTTPError(StatusResponse(
+@@ -363,15 +406,28 @@
"""
See L{IDAVResource.listProperties}.
"""
@@ -148,7 +156,7 @@
def listAllprop(self, request):
"""
Some DAV properties should not be returned to a C{DAV:allprop} query.
-@@ -465,8 +525,22 @@
+@@ -465,8 +521,22 @@
return super(DAVPropertyMixIn, self).displayName()
class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
@@ -171,7 +179,7 @@
##
# DAV
##
-@@ -509,6 +583,9 @@
+@@ -509,6 +579,9 @@
reactor.callLater(0, getChild)
def checkPrivileges(child):
@@ -181,7 +189,7 @@
if privileges is None:
return child
-@@ -517,14 +594,17 @@
+@@ -517,14 +590,17 @@
return d
def gotChild(child, childpath):
@@ -206,7 +214,7 @@
reactor.callLater(0, getChild)
-@@ -535,10 +615,10 @@
+@@ -535,10 +611,10 @@
completionDeferred.callback(None)
else:
childpath = joinURL(basepath, childname)
@@ -221,7 +229,7 @@
getChild()
-@@ -564,19 +644,21 @@
+@@ -564,19 +640,21 @@
See L{IDAVResource.authorize}.
"""
def onError(failure):
@@ -248,7 +256,7 @@
response = UnauthorizedResponse(request.credentialFactories,
request.remoteAddr)
else:
-@@ -587,7 +669,7 @@
+@@ -587,7 +665,7 @@
# class is supposed to be a FORBIDDEN status code and
# "Authorization will not help" according to RFC2616
#
@@ -257,7 +265,7 @@
d = self.checkPrivileges(request, privileges, recurse)
d.addErrback(onErrors)
-@@ -600,16 +682,21 @@
+@@ -600,16 +678,21 @@
def authenticate(self, request):
def loginSuccess(result):
@@ -283,7 +291,7 @@
authHeader = request.headers.getHeader('authorization')
-@@ -625,9 +712,10 @@
+@@ -625,9 +708,10 @@
# Try to match principals in each principal collection on the resource
def gotDetails(details):
@@ -297,7 +305,7 @@
def login(pcreds):
d = request.portal.login(pcreds, None, *request.loginInterfaces)
-@@ -635,13 +723,15 @@
+@@ -635,13 +719,15 @@
return d
@@ -317,7 +325,7 @@
##
# ACL
-@@ -650,49 +740,23 @@
+@@ -650,49 +736,23 @@
def currentPrincipal(self, request):
"""
@param request: the request being processed.
@@ -376,7 +384,7 @@
"""
@return: the L{davxml.ACL} element containing the default access control
list for this resource.
-@@ -704,6 +768,17 @@
+@@ -704,6 +764,17 @@
#
return readonlyACL
@@ -394,7 +402,7 @@
def setAccessControlList(self, acl):
"""
See L{IDAVResource.setAccessControlList}.
-@@ -1032,9 +1107,9 @@
+@@ -1032,9 +1103,9 @@
if myURL == "/":
# If we get to the root without any ACLs, then use the default.
@@ -406,7 +414,7 @@
# Dynamically update privileges for those ace's that are inherited.
if inheritance:
-@@ -1070,7 +1145,7 @@
+@@ -1070,7 +1141,7 @@
# Adjust ACE for inherit on this resource
children = list(ace.children)
children.remove(TwistedACLInheritable())
@@ -415,7 +423,7 @@
aces.append(davxml.ACE(*children))
else:
aces.extend(inherited_aces)
-@@ -1122,7 +1197,7 @@
+@@ -1122,7 +1193,7 @@
# Adjust ACE for inherit on this resource
children = list(ace.children)
children.remove(TwistedACLInheritable())
@@ -424,7 +432,7 @@
aces.append(davxml.ACE(*children))
# Filter out those that do not have a principal match with the current principal
-@@ -1146,49 +1221,69 @@
+@@ -1146,49 +1217,69 @@
This implementation returns an empty set.
"""
@@ -522,7 +530,7 @@
def samePrincipal(self, principal1, principal2):
"""
Check whether the two prinicpals are exactly the same in terms of
-@@ -1213,7 +1308,6 @@
+@@ -1213,7 +1304,6 @@
return False
def matchPrincipal(self, principal1, principal2, request):
@@ -530,7 +538,7 @@
"""
Check whether the principal1 is a principal in the set defined by
principal2.
-@@ -1238,6 +1332,9 @@
+@@ -1238,6 +1328,9 @@
if isinstance(principal1, davxml.Unauthenticated):
yield False
return
@@ -540,7 +548,7 @@
else:
yield True
return
-@@ -1265,7 +1362,6 @@
+@@ -1265,7 +1358,6 @@
assert principal2 is not None, "principal2 is None"
@@ -548,7 +556,7 @@
# Compare two HRefs and do group membership test as well
if principal1 == principal2:
yield True
-@@ -1296,9 +1392,9 @@
+@@ -1296,9 +1388,9 @@
def testGroup(group):
# Get principal resource for principal2
if group and isinstance(group, DAVPrincipalResource):
@@ -561,7 +569,7 @@
return False
-@@ -1426,7 +1522,7 @@
+@@ -1426,7 +1518,7 @@
log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
yield None
return
@@ -570,7 +578,7 @@
if isinstance(principal, davxml.HRef):
yield principal
-@@ -1511,6 +1607,265 @@
+@@ -1511,6 +1603,265 @@
return None
##
@@ -836,7 +844,7 @@
# HTTP
##
-@@ -1558,10 +1913,10 @@
+@@ -1558,10 +1909,10 @@
"""
DAV resource with no children.
"""
@@ -849,7 +857,7 @@
"""
Resource representing a WebDAV principal. (RFC 3744, section 2)
"""
-@@ -1571,7 +1926,7 @@
+@@ -1571,7 +1922,7 @@
# WebDAV
##
@@ -858,7 +866,7 @@
(dav_namespace, "alternate-URI-set"),
(dav_namespace, "principal-URL" ),
(dav_namespace, "group-member-set" ),
-@@ -1579,14 +1934,11 @@
+@@ -1579,14 +1930,11 @@
)
def davComplianceClasses(self):
@@ -874,7 +882,7 @@
def readProperty(self, property, request):
def defer():
if type(property) is tuple:
-@@ -1604,10 +1956,10 @@
+@@ -1604,10 +1952,10 @@
return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
if name == "group-member-set":
@@ -887,7 +895,7 @@
if name == "resourcetype":
if self.isCollection():
-@@ -1673,6 +2025,24 @@
+@@ -1673,6 +2021,24 @@
else:
return uri in self.groupMembers()
@@ -912,7 +920,7 @@
class AccessDeniedError(Exception):
def __init__(self, errors):
"""
-@@ -1712,6 +2082,37 @@
+@@ -1712,6 +2078,48 @@
davxml.registerElement(TwistedACLInheritable)
davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
@@ -947,6 +955,17 @@
+
+davxml.registerElement(TwistedQuotaUsedProperty)
+
++"""
++Indicates the Python class used, a type derived from DAVResource, that represents the WebDAV
++resource on which it is defined as a property.
++"""
++class TwistedResourceClass (davxml.WebDAVTextElement):
++ namespace = twisted_dav_namespace
++ name = "resource-class"
++ hidden = False
++
++davxml.registerElement(TwistedResourceClass)
++
allACL = davxml.ACL(
davxml.ACE(
davxml.Principal(davxml.All()),
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/extensions.py 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/extensions.py 2007-02-28 22:06:40 UTC (rev 1295)
@@ -15,7 +15,17 @@
#
# DRI: Wilfredo Sanchez, wsanchez at apple.com
##
+from twisted.web2.dav.element.base import dav_namespace
+from twisted.web2.dav.util import parentForURL
+from twisted.web2.dav.resource import TwistedACLInheritable
+from twisted.web2.dav.util import joinURL
+from twisted.internet import reactor
+from twisted.web2.dav.resource import AccessDeniedError
+from twisted.internet.defer import Deferred
+from twisted.web2.http_headers import generateContentType
+from twistedcaldav.sqlprops import sqlPropertyStore
+
"""
Extensions to web2.dav
"""
@@ -29,6 +39,7 @@
import urllib
import cgi
+import os
import time
from twisted.python import log
@@ -44,6 +55,7 @@
from twisted.web2.dav.static import DAVFile as SuperDAVFile
from twisted.web2.dav.resource import DAVResource as SuperDAVResource
from twisted.web2.dav.resource import DAVPrincipalResource as SuperDAVPrincipalResource
+from twisted.web2.dav.resource import TwistedResourceClass
from twistedcaldav.directory.sudo import SudoDirectoryService
@@ -69,13 +81,475 @@
return super(SudoAuthIDMixin, self).findPrincipalForAuthID(authid)
-class DAVResource (SudoAuthIDMixin, SuperDAVResource):
+class DAVPropertyHandlerMixin(object):
+
+ def create(self):
+ self.updateProperties()
+
+ def update(self):
+ self.updateProperties()
+
+ def updateProperties(self):
+ """
+ Push "live" properties into the dead property store".
+ """
+
+ props_to_set = []
+ # resourcetype
+ if self.isCollection():
+ props_to_set.append(davxml.ResourceType.collection)
+ else:
+ props_to_set.append(davxml.ResourceType.empty)
+
+ # getetag
+ etag = self.etag()
+ if etag is not None:
+ props_to_set.append(davxml.GETETag(etag.generate()))
+
+ # getcontenttype
+ mimeType = self.contentType()
+ if mimeType is not None:
+ mimeType.params = None # WebDAV getcontenttype property does not include parameters
+ props_to_set.append(davxml.GETContentType(generateContentType(mimeType)))
+
+ # getcontentlength
+ length = self.contentLength()
+ if length is None:
+ # TODO: really we should "render" the resource and
+ # determine its size from that but for now we just
+ # return an empty element.
+ props_to_set.append(davxml.GETContentLength(""))
+ else:
+ props_to_set.append(davxml.GETContentLength(str(length)))
+
+ # getlastmodified
+ lastModified = self.lastModified()
+ if lastModified is not None:
+ props_to_set.append(davxml.GETLastModified.fromDate(lastModified))
+
+ # creationdate
+ creationDate = self.creationDate()
+ if creationDate is not None:
+ props_to_set.append(davxml.CreationDate.fromDate(creationDate))
+
+ # displayname
+ displayName = self.displayName()
+ if displayName is not None:
+ props_to_set.append(davxml.DisplayName(displayName))
+
+ # acl
+ props_to_set.append(self.defaultAccessControlList())
+ #from twistedcaldav.static import calendarPrivilegeSet
+ #props_to_set.append(calendarPrivilegeSet)
+
+ # resource class
+ props_to_set.append(TwistedResourceClass(self.__class__.__name__))
+
+ # Now write out all properties in one go
+ self.deadProperties().setSeveral(props_to_set)
+
+ def isClassProperty(self, property):
+ if isinstance(property, tuple):
+ qname = property
+ else:
+ qname = property.qname()
+
+ return qname in ((dav_namespace, "supported-privilege-set"),)
+
+ def computeClassProperty(self, classname, property):
+ from twistedcaldav.static import calendarPrivilegeSet
+
+ if isinstance(property, tuple):
+ namespace, name = property
+ else:
+ namespace, name = property.qname()
+
+ if namespace == dav_namespace:
+ if name == "supported-privilege-set":
+ return calendarPrivilegeSet
+
+class DAVFastACLMixin(object):
+
+ def findChildNames(self, request, callbackAllowed, callbackForbidden=None, privileges=None, inherited_aces=None):
+
+ from twistedcaldav.resource import AccessDisabled
+ from twistedcaldav.static import calendarPrivilegeSet
+
+ # Get aces for children
+ acls = self.deadProperties().getSeveralResources((
+ davxml.ACL.qname(),
+ TwistedResourceClass.qname(),
+ davxml.GETETag.qname(),
+ AccessDisabled.qname(),
+ ))
+ aclmap = {}
+ for name, props in acls.iteritems():
+ if props.has_key(AccessDisabled.qname()):
+ if callbackForbidden:
+ callbackForbidden(name)
+ continue
+ acl = props.get(davxml.ACL.qname(), None)
+ privyset = self.computeClassProperty(props.get(TwistedResourceClass.qname(), None), (dav_namespace, "supported-privilege-set"))
+ aclmap.setdefault((acl, privyset), []).append(name)
+
+ # Now determine whether each ace satisfies privileges
+ for (acl, privyset), names in aclmap.iteritems():
+ checked = waitForDeferred(self.checkACLPrivilege(request, acl, privyset, privileges, inherited_aces))
+ yield checked
+ checked = checked.getResult()
+ if checked:
+ for name in names:
+ callbackAllowed(name)
+ elif callbackForbidden:
+ for name in names:
+ callbackForbidden(name)
+
+ yield None
+
+ findChildNames = deferredGenerator(findChildNames)
+
+ def checkACLPrivilege(self, request, acl, privyset, privileges, inherited_aces):
+
+ principal = self.currentPrincipal(request)
+
+ # Other principals types don't make sense as actors.
+ assert (
+ principal.children[0].name in ("unauthenticated", "href"),
+ "Principal is not an actor: %r" % (principal,)
+ )
+
+ acl = self.fullAccessControlList(acl, inherited_aces)
+
+ pending = list(privileges)
+ denied = []
+
+ for ace in acl.children:
+ for privilege in tuple(pending):
+ if not self.matchPrivilege(davxml.Privilege(privilege), ace.privileges, privyset):
+ continue
+
+ match = waitForDeferred(self.matchPrincipal(principal, ace.principal, request))
+ yield match
+ match = match.getResult()
+
+ if match:
+ if ace.invert:
+ continue
+ else:
+ if not ace.invert:
+ continue
+
+ pending.remove(privilege)
+
+ if not ace.allow:
+ denied.append(privilege)
+
+ yield len(denied) + len(pending) == 0
+
+ checkACLPrivilege = deferredGenerator(checkACLPrivilege)
+
+ def fullAccessControlList(self, acl, inherited_aces):
+ """
+ See L{IDAVResource.accessControlList}.
+
+ This implementation looks up the ACL in the private property
+ C{(L{twisted_private_namespace}, "acl")}.
+ If no ACL has been stored for this resource, it returns the value
+ returned by C{defaultAccessControlList}.
+ If access is disabled it will return C{None}.
+ """
+ #
+ # Inheritance is problematic. Here is what we do:
+ #
+ # 1. A private element <Twisted:inheritable> is defined for use inside
+ # of a <DAV:ace>. This private element is removed when the ACE is
+ # exposed via WebDAV.
+ #
+ # 2. When checking ACLs with inheritance resolution, the server must
+ # examine all parent resources of the current one looking for any
+ # <Twisted:inheritable> elements.
+ #
+ # If those are defined, the relevant ace is applied to the ACL on the
+ # current resource.
+ #
+
+ # Dynamically update privileges for those ace's that are inherited.
+ if acl:
+ aces = list(acl.children)
+ else:
+ aces = []
+
+ aces.extend(inherited_aces)
+
+ acl = davxml.ACL(*aces)
+
+ return acl
+
+ def findChildrenBatch(self, depth, request, callback, privileges=None, inherited_aces=None):
+ """
+ See L{IDAVResource.findChildren}.
+
+ This implementation works for C{depth} values of C{"0"}, C{"1"},
+ and C{"infinity"}. As long as C{self.listChildren} is implemented
+ """
+ assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
+
+ if depth == "0" or not self.isCollection():
+ return succeed(None)
+
+ # Get aces for children
+ aclprops = self.deadProperties().getSeveralResources((
+ davxml.ACL.qname(),
+ ))
+ acls = {}
+ for name, props in acls.iteritems():
+ acls[name] = props.get(davxml.ACL.qname(), None)
+
+ completionDeferred = Deferred()
+ basepath = request.urlForResource(self)
+ children = list(self.listChildren())
+
+ def checkPrivilegesError(failure):
+ failure.trap(AccessDeniedError)
+ reactor.callLater(0, getChild)
+
+ def checkPrivileges(child):
+ if child is None:
+ return None
+
+ if privileges is None:
+ return child
+
+ cname = os.path.basename(request.urlForResource(self))
+ d = child.checkPrivilegesBatch(request, privileges, inherited_aces=inherited_aces, acl=acls.get(cname, None))
+ d.addCallback(lambda _: child)
+ return d
+
+ def gotChild(child, childpath):
+ if child is None:
+ callback(None, childpath + "/")
+ else:
+ if child.isCollection():
+ callback(child, childpath + "/")
+ if depth == "infinity":
+ d = child.findChildren(depth, request, callback, privileges)
+ d.addCallback(lambda x: reactor.callLater(0, getChild))
+ return d
+ else:
+ callback(child, childpath)
+
+ reactor.callLater(0, getChild)
+
+ def getChild():
+ try:
+ childname = children.pop()
+ except IndexError:
+ completionDeferred.callback(None)
+ else:
+ childpath = joinURL(basepath, childname)
+ d = request.locateChildResource(self, childname)
+ d.addCallback(checkPrivileges)
+ d.addCallbacks(gotChild, checkPrivilegesError, (childpath,))
+ d.addErrback(completionDeferred.errback)
+
+ getChild()
+
+ return completionDeferred
+
+ def checkPrivilegesBatch(self, request, privileges, recurse=False, principal=None, inherited_aces=None, acl=None):
+ """
+ Check whether the given principal has the given privileges.
+ (RFC 3744, section 5.5)
+ @param request: the request being processed.
+ @param privileges: an iterable of L{davxml.WebDAVElement} elements
+ denoting access control privileges.
+ @param recurse: C{True} if a recursive check on all child
+ resources of this resource should be performed as well,
+ C{False} otherwise.
+ @param principal: the L{davxml.Principal} to check privileges
+ for. If C{None}, it is deduced from C{request} by calling
+ L{currentPrincipal}.
+ @param inherited_aces: a list of L{davxml.ACE}s corresponding to the precomputed
+ inheritable aces from the parent resource hierarchy.
+ @return: a L{Deferred} that callbacks with C{None} or errbacks with an
+ L{AccessDeniedError}
+ """
+ if principal is None:
+ principal = self.currentPrincipal(request)
+
+ supportedPrivs = waitForDeferred(self.supportedPrivileges(request))
+ yield supportedPrivs
+ supportedPrivs = supportedPrivs.getResult()
+
+ # Other principals types don't make sense as actors.
+ assert (
+ principal.children[0].name in ("unauthenticated", "href"),
+ "Principal is not an actor: %r" % (principal,)
+ )
+
+ errors = []
+
+ resources = [(self, None)]
+
+ if recurse:
+ x = self.findChildren("infinity", request, lambda x, y: resources.append((x,y)))
+ x = waitForDeferred(x)
+ yield x
+ x.getResult()
+
+ for resource, uri in resources:
+ acl = waitForDeferred(resource.accessControlListBatch(request, inherited_aces=inherited_aces, acl=acl))
+ yield acl
+ acl = acl.getResult()
+
+ # Check for disabled
+ if acl is None:
+ errors.append((uri, list(privileges)))
+ continue
+
+ pending = list(privileges)
+ denied = []
+
+ for ace in acl.children:
+ for privilege in tuple(pending):
+ if not self.matchPrivilege(davxml.Privilege(privilege), ace.privileges, supportedPrivs):
+ continue
+
+ match = waitForDeferred(self.matchPrincipal(principal, ace.principal, request))
+ yield match
+ match = match.getResult()
+
+ if match:
+ if ace.invert:
+ continue
+ else:
+ if not ace.invert:
+ continue
+
+ pending.remove(privilege)
+
+ if not ace.allow:
+ denied.append(privilege)
+
+ denied += pending # If no matching ACE, then denied
+
+ if denied:
+ errors.append((uri, denied))
+
+ if errors:
+ raise AccessDeniedError(errors,)
+
+ yield None
+
+ checkPrivilegesBatch = deferredGenerator(checkPrivilegesBatch)
+
+ def accessControlListBatch(self, request, inheritance=True, expanding=False, inherited_aces=None, acl=None):
+ """
+ See L{IDAVResource.accessControlList}.
+
+ This implementation looks up the ACL in the private property
+ C{(L{twisted_private_namespace}, "acl")}.
+ If no ACL has been stored for this resource, it returns the value
+ returned by C{defaultAccessControlList}.
+ If access is disabled it will return C{None}.
+ """
+ #
+ # Inheritance is problematic. Here is what we do:
+ #
+ # 1. A private element <Twisted:inheritable> is defined for use inside
+ # of a <DAV:ace>. This private element is removed when the ACE is
+ # exposed via WebDAV.
+ #
+ # 2. When checking ACLs with inheritance resolution, the server must
+ # examine all parent resources of the current one looking for any
+ # <Twisted:inheritable> elements.
+ #
+ # If those are defined, the relevant ace is applied to the ACL on the
+ # current resource.
+ #
+ myURL = None
+
+ def getMyURL():
+ url = request.urlForResource(self)
+
+ assert url is not None, "urlForResource(self) returned None for resource %s" % (self,)
+
+ return url
+
+ if acl is None:
+ # Produce a sensible default for an empty ACL.
+ if myURL is None:
+ myURL = getMyURL()
+
+ if myURL == "/":
+ # If we get to the root without any ACLs, then use the default.
+ acl = self.defaultRootAccessControlList()
+ else:
+ acl = self.defaultAccessControlList()
+
+ # Dynamically update privileges for those ace's that are inherited.
+ if inheritance:
+ aces = list(acl.children)
+
+ if myURL is None:
+ myURL = getMyURL()
+
+ if inherited_aces is None:
+ if myURL != "/":
+ parentURL = parentForURL(myURL)
+
+ parent = waitForDeferred(request.locateResource(parentURL))
+ yield parent
+ parent = parent.getResult()
+
+ if parent:
+ parent_acl = waitForDeferred(
+ parent.accessControlList(request, inheritance=True, expanding=True)
+ )
+ yield parent_acl
+ parent_acl = parent_acl.getResult()
+
+ # Check disabled
+ if parent_acl is None:
+ yield None
+ return
+
+ for ace in parent_acl.children:
+ if ace.inherited:
+ aces.append(ace)
+ elif TwistedACLInheritable() in ace.children:
+ # Adjust ACE for inherit on this resource
+ children = list(ace.children)
+ children.remove(TwistedACLInheritable())
+ children.append(davxml.Inherited(davxml.HRef(parentURL)))
+ aces.append(davxml.ACE(*children))
+ else:
+ aces.extend(inherited_aces)
+
+ # Always filter out any remaining private properties when we are
+ # returning the ACL for the final resource after doing parent
+ # inheritance.
+ if not expanding:
+ aces = [
+ davxml.ACE(*[
+ c for c in ace.children
+ if c != TwistedACLInheritable()
+ ])
+ for ace in aces
+ ]
+
+ acl = davxml.ACL(*aces)
+
+ yield acl
+
+ accessControlListBatch = deferredGenerator(accessControlListBatch)
+
+class DAVResource (SudoAuthIDMixin, DAVPropertyHandlerMixin, DAVFastACLMixin, SuperDAVResource):
"""
Extended L{twisted.web2.dav.resource.DAVResource} implementation.
"""
-
-class DAVPrincipalResource (SuperDAVPrincipalResource):
+class DAVPrincipalResource (DAVPropertyHandlerMixin, DAVFastACLMixin, SuperDAVPrincipalResource):
"""
Extended L{twisted.web2.dav.static.DAVFile} implementation.
"""
@@ -100,10 +574,15 @@
return davxml.ResourceType(davxml.Principal())
-class DAVFile (SudoAuthIDMixin, SuperDAVFile):
+class DAVFile (SudoAuthIDMixin, DAVPropertyHandlerMixin, DAVFastACLMixin, SuperDAVFile):
"""
Extended L{twisted.web2.dav.static.DAVFile} implementation.
"""
+ def deadProperties(self):
+ if not hasattr(self, "_dead_properties"):
+ self._dead_properties = sqlPropertyStore(self)
+ return self._dead_properties
+
def readProperty(self, property, request):
if type(property) is tuple:
qname = property
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/method/put_common.py 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/method/put_common.py 2007-02-28 22:06:40 UTC (rev 1295)
@@ -434,6 +434,12 @@
yield response
response = response.getResult()
+ # Reset dead properties
+ if overwrite:
+ destination.update()
+ else:
+ destination.create()
+
# Copy dead properties first, before adding overridden values
if source is not None:
properties = source.deadProperties().getAll()
@@ -445,11 +451,14 @@
if source.hasDeadProperty(TwistedGETContentMD5):
md5 = source.readDeadProperty(TwistedGETContentMD5)
destination.writeDeadProperty(md5)
+ etag = source.readDeadProperty(davxml.GETETag)
+ destination.writeDeadProperty(etag)
else:
# Finish MD5 calc and write dead property
md5.close()
md5 = md5.getMD5()
destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
+ destination.writeDeadProperty(davxml.GETETag.fromString(md5))
response = IResponse(response)
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/sqlprops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/sqlprops.py 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/sqlprops.py 2007-02-28 22:06:40 UTC (rev 1295)
@@ -38,7 +38,6 @@
from twisted.web2 import responsecode
from twisted.web2.http import HTTPError, StatusResponse
-from twistedcaldav.root import RootResource
from twistedcaldav.sql import AbstractSQLDatabase
DEBUG_LOG = False
@@ -51,6 +50,7 @@
def __init__(self, resource):
self.resource = resource
if os.path.exists(os.path.dirname(resource.fp.path)):
+ from twistedcaldav.root import RootResource
if resource.isCollection() and isinstance(resource, RootResource):
self.rname = ""
indexpath = resource.fp.path
@@ -58,8 +58,14 @@
self.rname = os.path.basename(resource.fp.path)
indexpath = os.path.dirname(resource.fp.path)
self.index = SQLPropertiesDatabase(indexpath)
+ if resource.isCollection():
+ self.childindex = SQLPropertiesDatabase(resource.fp.path)
+ else:
+ self.childindex = None
else:
+ log.msg("No sqlPropertyStore file for %s" % (os.path.dirname(resource.fp.path),))
self.index = None
+ self.childindex = None
def get(self, qname):
"""
@@ -100,11 +106,11 @@
return self.index.getSeveralPropertyValues(self.rname, qnames)
- def getAll(self):
+ def getAll(self, nohidden=True):
"""
Read all properties from index.
- @param qnames: C{list} of C{tuple} of property namespace and name.
+ @param nohidden: C{True} to not return hidden properties, C{False} otherwise.
@return: a C{dict} containing property name/value.
"""
if not self.index:
@@ -113,7 +119,7 @@
"No properties"
))
- return self.index.getAllPropertyValues(self.rname)
+ return self.index.getAllPropertyValues(self.rname, nohidden)
def getSeveralResources(self, qnames):
"""
@@ -125,14 +131,30 @@
if not qnames:
return None
- if not self.index:
+ if not self.childindex:
raise HTTPError(StatusResponse(
responsecode.NOT_FOUND,
"No such property: {%s}%s" % qnames[0]
))
- return self.index.getSeveralResourcePropertyValues(qnames)
+ return self.childindex.getSeveralResourcePropertyValues(qnames)
+ def getAllResources(self, nohidden=True):
+ """
+ Read specific properties for all child resources from index.
+
+ @param nohidden: C{True} to not return hidden properties, C{False} otherwise.
+ @return: a C{dict} with resource name as keys and C{dict} of property name/classes as values
+ """
+
+ if not self.childindex:
+ raise HTTPError(StatusResponse(
+ responsecode.NOT_FOUND,
+ "No proeprties"
+ ))
+
+ return self.childindex.getAllResourcePropertyValues(nohidden)
+
def set(self, property):
"""
Write property into index.
@@ -141,7 +163,7 @@
"""
if self.index:
- self.index.setOnePropertyValue(self.rname, property.qname(), property)
+ self.index.setOnePropertyValue(self.rname, property.qname(), property, property.hidden)
def setSeveral(self, properties):
"""
@@ -151,7 +173,7 @@
"""
if self.index:
- self.index.setSeveralPropertyValues(self.rname, [(p.qname(), p) for p in properties])
+ self.index.setSeveralPropertyValues(self.rname, [(p.qname(), p, p.hidden) for p in properties])
def delete(self, qname):
"""
@@ -209,6 +231,19 @@
else:
return ()
+ def listAll(self):
+ """
+ List all property names for children of this resource.
+
+ SELECT RESOURCENAME, PROPNAME from PROPERTIES
+
+ """
+
+ if self.childindex:
+ return self.childindex.listAllProperties()
+ else:
+ return {}
+
def search(self, qname, text):
"""
Search a specific property.
@@ -285,18 +320,19 @@
path = os.path.join(path, SQLPropertiesDatabase.dbFilename)
super(SQLPropertiesDatabase, self).__init__(path, SQLPropertiesDatabase.dbFormatVersion, utf8=True)
- def setOnePropertyValue(self, rname, pname, pvalue):
+ def setOnePropertyValue(self, rname, pname, pvalue, hidden):
"""
Add a property.
@param rname: a C{str} containing the resource name.
@param pname: a C{str} containing the name of the property to set.
+ @param hidden: C{True} for a hidden proeprty, C{False} otehrwise.
@param pvalue: a C{str} containing the property value to set.
"""
# Remove what is there, then add it back.
self._delete_from_db(rname, self._encode(pname))
- self._add_to_db(rname, self._encode(pname), cPickle.dumps(pvalue), pvalue.toxml())
+ self._add_to_db(rname, self._encode(pname), cPickle.dumps(pvalue), pvalue.toxml(), hidden)
self._db_commit()
def setSeveralPropertyValues(self, rname, properties):
@@ -311,7 +347,7 @@
# Remove what is there, then add it back.
for p in properties:
self._delete_from_db(rname, self._encode(p[0]))
- self._add_to_db(rname, self._encode(p[0]), cPickle.dumps(p[1]), p[1].toxml())
+ self._add_to_db(rname, self._encode(p[0]), cPickle.dumps(p[1]), p[1].toxml(), p[2])
self._db_commit()
def getOnePropertyValue(self, rname, pname):
@@ -350,12 +386,12 @@
if DEBUG_LOG:
log.msg("getSeveralPropertyValues: %s \"%s\"" % (self.dbpath, pnames))
properties = {}
- statement = "select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1 and ("
+ statement = "select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1 and PROPERTYNAME in ("
args = [rname]
for i, pname in enumerate(pnames):
if i != 0:
- statement += " or "
- statement += "PROPERTYNAME=:%s" % (i + 2,)
+ statement += ", "
+ statement += ":%s" % (i + 2,)
args.append(self._encode(pname))
statement += ")"
@@ -364,19 +400,21 @@
return properties
- def getAllPropertyValues(self, rname):
+ def getAllPropertyValues(self, rname, nohidden):
"""
Get specified property values from specific resource.
@param rname: a C{str} containing the resource name.
+ @param nohidden: C{True} to not return hidden properties, C{False} otherwise.
@return: a C{dict} containing property name/value.
"""
- # Remove what is there, then add it back.
- if DEBUG_LOG:
- log.msg("getAllPropertyValues: %s" % (self.dbpath,))
properties = {}
- for row in self._db_execute("select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1", rname):
+ if nohidden:
+ statement = "select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1 and HIDDEN = 'F'"
+ else:
+ statement = "select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1"
+ for row in self._db_execute(statement, rname):
properties[self._decode(row[0])] = cPickle.loads(row[1])
return properties
@@ -393,19 +431,38 @@
if DEBUG_LOG:
log.msg("getAllPropertyValues: %s \"%s\"" % (self.dbpath, pnames))
members = {}
- statement = "select RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where "
+ statement = "select RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where PROPERTYNAME in ("
args = []
for i, pname in enumerate(pnames):
if i != 0:
- statement += " or "
- statement += "PROPERTYNAME=:%s" % (i + 1,)
+ statement += ", "
+ statement += ":%s" % (i + 1,)
args.append(self._encode(pname))
+ statement += ")"
for row in self._db_execute(statement, *args):
members.setdefault(row[0], {})[self._decode(row[1])] = cPickle.loads(row[2])
return members
+ def getAllResourcePropertyValues(self, nohidden):
+ """
+ Get specified property values from all resources.
+
+ @param nohidden: C{True} to not return hidden properties, C{False} otherwise.
+ @return: a C{dict} containing C{str} keys (names of child resources) and C{dict} values of property name/value.
+ """
+
+ members = {}
+ if nohidden:
+ statement = "select RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where HIDDEN='F'"
+ else:
+ statement = "select RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT from PROPERTIES"
+ for row in self._db_execute(statement):
+ members.setdefault(row[0], {})[self._decode(row[1])] = cPickle.loads(row[2])
+
+ return members
+
def removeProperty(self, rname, pname):
"""
Remove a property.
@@ -455,6 +512,19 @@
members.add(self._decode(row[0]))
return members
+ def listAllProperties(self):
+ """
+ List all properties in child resources.
+
+ @param rname: a C{str} containing the resource name.
+ @return: a C{dict} containing the resource names and property names.
+ """
+
+ results = {}
+ for row in self._db_execute("select RESOURCENAME, PROPERTYNAME from PROPERTIES"):
+ results.setdefault(row[0], set()).add(self._decode(row[1]))
+ return results
+
def searchOneProperty(self, pname, text):
results = {}
liketext = "%%%s%%" % (text,)
@@ -487,7 +557,7 @@
results.setdefault(row[0], set()).add(self._decode(row[1]))
return results
- def _add_to_db(self, rname, pname, pobject, pvalue):
+ def _add_to_db(self, rname, pname, pobject, pvalue, hidden):
"""
Add a property.
@@ -497,11 +567,15 @@
@param pvalue: a C{str} containing the text of the property value to set.
"""
+ if hidden:
+ hidden_value = "T"
+ else:
+ hidden_value = "F"
self._db_execute(
"""
- insert into PROPERTIES (RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT, PROPERTYVALUE)
- values (:1, :2, :3, :4)
- """, rname, pname, pobject, pvalue
+ insert into PROPERTIES (RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT, PROPERTYVALUE, HIDDEN)
+ values (:1, :2, :3, :4, :5)
+ """, rname, pname, pobject, pvalue, hidden_value
)
def _delete_all_from_db(self, rname):
@@ -547,7 +621,8 @@
RESOURCENAME text,
PROPERTYNAME text,
PROPERTYOBJECT text,
- PROPERTYVALUE text
+ PROPERTYVALUE text,
+ HIDDEN text(1)
)
"""
)
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/static.py 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/static.py 2007-02-28 22:06:40 UTC (rev 1295)
@@ -147,6 +147,7 @@
if status != responsecode.CREATED:
raise HTTPError(status)
+ self.create()
self.writeDeadProperty(resourceType)
return status
@@ -412,6 +413,8 @@
fp.open("w").close()
fp.restat(False)
+ self.create()
+
return True
class CalendarHomeProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeProvisioningResource, DAVFile):
Modified: CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/tap.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/tap.py 2007-02-28 19:26:14 UTC (rev 1294)
+++ CalendarServer/branches/users/cdaboo/sqlprops-1202/twistedcaldav/tap.py 2007-02-28 22:06:40 UTC (rev 1295)
@@ -328,6 +328,10 @@
root.putChild('principals', principalCollection)
root.putChild('calendars', calendarCollection)
+ root.create()
+ #principalCollection.provision()
+ #calendarCollection.provision()
+
# Configure default ACLs on the root resource
log.msg("Setting up default ACEs on root resource")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070228/963f757d/attachment.html
More information about the calendarserver-changes
mailing list