[CalendarServer-changes] [9979] CalendarServer/branches/users/cdaboo/ischedule-dkim
source_changes at macosforge.org
source_changes at macosforge.org
Wed Oct 24 12:57:08 PDT 2012
Revision: 9979
http://trac.calendarserver.org//changeset/9979
Author: cdaboo at apple.com
Date: 2012-10-24 12:57:08 -0700 (Wed, 24 Oct 2012)
Log Message:
-----------
Merge from trunk.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/provision/root.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/shell/vfs.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/calendarpromotion.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/doc/Admin/ExtendedLogItems.rst
CalendarServer/branches/users/cdaboo/ischedule-dkim/twext/web2/resource.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcalendar.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcol.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/resource.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/icaldiff.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/imip/mailgateway.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/implicit.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/test/test_icaldiff.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/test/test_wrapping.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/base/propertystore/sql.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/common.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/test_file.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/icalendarstore.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/common.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/test_file.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/iaddressbookstore.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/file.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/test/test_sql.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/__init__.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/base.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/test/test_promotion.py
CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/common.py
Property Changed:
----------------
CalendarServer/branches/users/cdaboo/ischedule-dkim/
Property changes on: CalendarServer/branches/users/cdaboo/ischedule-dkim
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:9747-9936
+ /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:9747-9978
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/provision/root.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/provision/root.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/provision/root.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -384,14 +384,6 @@
"Your client software (%s) is not allowed to access this service." % (agent,)
))
- # Look for forwarding
- remote_ip = request.headers.getRawHeaders('x-forwarded-for')
- if remote_ip and len(remote_ip) == 1:
- request.forwarded_for = remote_ip[0]
- if not hasattr(request, "extendedLogItems"):
- request.extendedLogItems = {}
- request.extendedLogItems["xff"] = remote_ip[0]
-
if config.EnableResponseCache and request.method == "PROPFIND" and not getattr(request, "notInCache", False) and len(segments) > 1:
try:
authnUser, authzUser = (yield self.authenticate(request))
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/shell/vfs.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/shell/vfs.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/shell/vfs.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -256,19 +256,43 @@
@inlineCallbacks
def list(self):
- result = set()
+ results = {}
# FIXME: This should be the merged total of calendar homes and address book homes.
# FIXME: Merge in directory UIDs also?
- # FIXME: Add directory info (eg. name) to listing
+ # FIXME: Add directory info (eg. name) to list entry
- for txn, home in (yield self.service.store.eachCalendarHome()):
- result.add(ListEntry(self, PrincipalHomeFolder, home.uid()))
+ def addResult(uid):
+ if uid in results:
+ return
- returnValue(result)
+ record = self.service.directory.recordWithUID(uid)
+ if record:
+ info = {
+ "Record Type": record.recordType,
+ "Short Name" : record.shortNames[0],
+ "Full Name" : record.fullName,
+ }
+ else:
+ info = {}
+ results[uid] = ListEntry(self, PrincipalHomeFolder, uid, **info)
+ txn = self.service.store.newTransaction()
+ try:
+ for home in (yield txn.calendarHomes()):
+ addResult(home.uid())
+ for home in (yield txn.addressbookHomes()):
+ addResult(home.uid())
+ finally:
+ (yield txn.abort())
+ returnValue(results.itervalues())
+
+ list.fieldNames = ("Record Name", "Short Name", "Full Name")
+
+
+
class RecordFolder(Folder):
def _recordForName(self, name):
recordTypeAttr = "recordType_" + self.recordType
@@ -358,67 +382,76 @@
if not hasattr(self, "_didInitChildren"):
txn = self.service.store.newTransaction()
- if (
- self.record is not None and
- self.service.config.EnableCalDAV and
- self.record.enabledForCalendaring
- ):
- create = True
- else:
- create = False
+ try:
+ if (
+ self.record is not None and
+ self.service.config.EnableCalDAV and
+ self.record.enabledForCalendaring
+ ):
+ create = True
+ else:
+ create = False
- # Try assuming it exists
- home = (yield txn.calendarHomeWithUID(self.uid, create=False))
+ # Try assuming it exists
+ home = (yield txn.calendarHomeWithUID(self.uid, create=False))
- if home is None and create:
- # Doesn't exist, so create it in a different
- # transaction, to avoid having to commit the live
- # transaction.
- txnTemp = self.service.store.newTransaction()
- home = (yield txnTemp.calendarHomeWithUID(self.uid, create=True))
- (yield txnTemp.commit())
+ if home is None and create:
+ # Doesn't exist, so create it in a different
+ # transaction, to avoid having to commit the live
+ # transaction.
+ txnTemp = self.service.store.newTransaction()
+ try:
+ home = (yield txnTemp.calendarHomeWithUID(self.uid, create=True))
+ (yield txnTemp.commit())
- # Fetch the home again. This time we expect it to be there.
- home = (yield txn.calendarHomeWithUID(self.uid, create=False))
- assert home
+ # Fetch the home again. This time we expect it to be there.
+ home = (yield txn.calendarHomeWithUID(self.uid, create=False))
+ assert home
+ finally:
+ (yield txn.abort())
- if home:
- self._children["calendars"] = CalendarHomeFolder(
- self.service,
- self.path + ("calendars",),
- home,
- self.record,
- )
+ if home:
+ self._children["calendars"] = CalendarHomeFolder(
+ self.service,
+ self.path + ("calendars",),
+ home,
+ self.record,
+ )
- if (
- self.record is not None and
- self.service.config.EnableCardDAV and
- self.record.enabledForAddressBooks
- ):
- create = True
- else:
- create = False
+ if (
+ self.record is not None and
+ self.service.config.EnableCardDAV and
+ self.record.enabledForAddressBooks
+ ):
+ create = True
+ else:
+ create = False
- # Again, assume it exists
- home = (yield txn.addressbookHomeWithUID(self.uid))
+ # Again, assume it exists
+ home = (yield txn.addressbookHomeWithUID(self.uid))
- if not home and create:
- # Repeat the above dance.
- txnTemp = self.service.store.newTransaction()
- home = (yield txnTemp.addressbookHomeWithUID(self.uid, create=True))
- (yield txnTemp.commit())
+ if not home and create:
+ # Repeat the above dance.
+ txnTemp = self.service.store.newTransaction()
+ try:
+ home = (yield txnTemp.addressbookHomeWithUID(self.uid, create=True))
+ (yield txnTemp.commit())
- # Fetch the home again. This time we expect it to be there.
- home = (yield txn.addressbookHomeWithUID(self.uid, create=False))
- assert home
+ # Fetch the home again. This time we expect it to be there.
+ home = (yield txn.addressbookHomeWithUID(self.uid, create=False))
+ assert home
+ finally:
+ (yield txn.abort())
- if home:
- self._children["addressbooks"] = AddressBookHomeFolder(
- self.service,
- self.path + ("addressbooks",),
- home,
- self.record,
- )
+ if home:
+ self._children["addressbooks"] = AddressBookHomeFolder(
+ self.service,
+ self.path + ("addressbooks",),
+ home,
+ self.record,
+ )
+ finally:
+ (yield txn.abort())
self._didInitChildren = True
@@ -462,8 +495,19 @@
@inlineCallbacks
def list(self):
calendars = (yield self.home.calendars())
- returnValue((ListEntry(self, CalendarFolder, c.name()) for c in calendars))
+ result = []
+ for calendar in calendars:
+ displayName = calendar.displayName()
+ if displayName is None:
+ displayName = "(unset)"
+ info = {
+ "Display Name": displayName,
+ "Sync Token" : (yield calendar.syncToken()),
+ }
+ result.append(ListEntry(self, CalendarFolder, calendar.name(), **info))
+ returnValue(result)
+
@inlineCallbacks
def describe(self):
description = ["Calendar home:\n"]
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/calendarpromotion.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/calendarpromotion.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/calendarpromotion.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -24,6 +24,23 @@
GROUP_NAME = "calendar"
LOG_DIR = "/var/log/caldavd"
+
+def updatePlist(plistData):
+ """
+ Update the passed-in plist data with new values for disabling the XMPPNotifier, and
+ to set the DSN to use the server-specific Postgres.
+
+ @param plistData: the plist data to update in place
+ @type plistData: C{dict}
+ """
+ try:
+ if plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"]:
+ plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"] = False
+ except KeyError:
+ pass
+ plistData["DSN"] = "/Library/Server/PostgreSQL For Server Services/Socket:caldav:caldav:::"
+
+
def main():
try:
@@ -45,17 +62,11 @@
if os.path.exists(plistPath):
try:
plistData = readPlist(plistPath)
+ updatePlist(plistData)
+ writePlist(plistData, plistPath)
- # Disable XMPPNotifier now that we're directly talking to APNS
- try:
- if plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"]:
- plistData["Notifications"]["Services"]["XMPPNotifier"]["Enabled"] = False
- writePlist(plistData, plistPath)
- except KeyError:
- pass
-
except Exception, e:
- print "Unable to disable XMPP in %s: %s" % (plistPath, e)
+ print "Unable to disable update values in %s: %s" % (plistPath, e)
else:
# Copy configuration
Copied: CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/test/test_promotion.py (from rev 9978, CalendarServer/trunk/contrib/migration/test/test_promotion.py)
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/test/test_promotion.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/contrib/migration/test/test_promotion.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -0,0 +1,60 @@
+##
+# Copyright (c) 2012 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.
+##
+
+import twistedcaldav.test.util
+from contrib.migration.calendarpromotion import updatePlist
+
+class PromotionTests(twistedcaldav.test.util.TestCase):
+ """
+ Calendar Server Promotion Tests
+ """
+
+ def test_updatePlist(self):
+ """
+ Verify XMPPNotifier is disabled and DSN is updated
+ """
+
+ orig = {
+ "ignored" : "ignored",
+ }
+ expected = {
+ "ignored" : "ignored",
+ "DSN" : "/Library/Server/PostgreSQL For Server Services/Socket:caldav:caldav:::",
+ }
+ updatePlist(orig)
+ self.assertEquals(orig, expected)
+
+ orig = {
+ "Notifications" : {
+ "Services" : {
+ "XMPPNotifier" : {
+ "Enabled" : True
+ }
+ }
+ }
+ }
+ expected = {
+ "Notifications" : {
+ "Services" : {
+ "XMPPNotifier" : {
+ "Enabled" : False
+ }
+ }
+ },
+ "DSN" : "/Library/Server/PostgreSQL For Server Services/Socket:caldav:caldav:::",
+ }
+ updatePlist(orig)
+ self.assertEquals(orig, expected)
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/doc/Admin/ExtendedLogItems.rst
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/doc/Admin/ExtendedLogItems.rst 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/doc/Admin/ExtendedLogItems.rst 2012-10-24 19:57:08 UTC (rev 9979)
@@ -21,7 +21,7 @@
``i``
- the port number of the server instance emitting the log
+ the index number of the server instance emitting the log; corresponds to the slave number shown in process title.
``t``
@@ -71,17 +71,27 @@
the value of the X-Forwarded-For header, if present
-In the following example, we see a ``CalDAV:calendar-multiget``
-``REPORT`` for 32 resources in a user's calendar, which was handled by
-instance ``8459`` in 183.0ms, with one outstanding request (the one
-being logged):
+ ``fb-cached``
+ When doing free-busy queries, this is the number of calendars queried for which free-busy info was already cached
+
+ ``fb-uncached``
+
+ When doing free-busy queries, this is the number of calendars queried for which free-busy info was NOT already cached
+
+ ``cl``
+
+ Content length, in bytes
+
+In the following example, we see a free-busy ``POST``
+requesting availability for two users, which was handled by
+instance ``1`` in 782.6i ms. This instance was only processing one request at the time this was logged (or=1). Of the two calendars targeted by the free-busy query, one already had free-busy info cached, while the other was not cached. (fb-cached=1, fb-uncached=1)
+
::
- 17.108.160.37 - scastillo [15/Sep/2009:20:10:23 +0000] "REPORT(CalDAV:calendar-multiget) /calendars/__uids__/B8CE9430-965B-11DE-B626-EC2E9DB52B69/calendar/ HTTP/1.1" 207 149285 "-" "DAVKit/4.0 (729); CalendarStore/4.0 (965); iCal/4.0 (1362); Mac OS X/10.6.1 (10B504)" i=8459 t=183.0 or=1 rcount=32
+ 10.1.5.43 - user5 [23/Oct/2012:13:42:56 -0700] "POST /calendars/__uids__/B2302CB9-D28F-4CB4-B3D9-0AF0FEDB8110/outbox/ HTTP/1.1" 200 1490 "-" "CalendarStore/5.0.2 (1166); iCal/5.0.2 (1571); Mac OS X/10.7.3 (11D50)" i=1 or=1 t=782.6 fb-uncached=1 fb-cached=1 recipients=2 cl=577
-
**Fine-grained request time logging**
If the configuration key EnableExtendedTimingAccessLog is set to true, additional key-value pairs will be logged with each request. The overall request time "t" is broken into four phases, and the elapsed time for each phase is logged. The new keys representing the four request phases are:
@@ -107,4 +117,4 @@
::
- 17.209.103.42 - wsanchez [24/Jul/2012:17:51:29 +0000] "REPORT(CalDAV:calendar-multiget) /calendars/__uids__/F114CA1D-295F-42A5-A5BD-D1A1B19FC049/60E68E32-4C87-4E63-9BF2-12A25E8F2623/ HTTP/1.1" 207 114349 "-" "CalendarStore/5.0.2 (1166); iCal/5.0.2 (1571); Mac OS X/10.7.3 (11D50d)" i=7 or=1 t=764.7 t-req-proc=4.8 t-resp-gen=754.5 t-resp-wr=5.1 t-log=0.2 rcount=2
\ No newline at end of file
+ 17.209.103.42 - wsanchez [24/Jul/2012:17:51:29 +0000] "REPORT(CalDAV:calendar-multiget) /calendars/__uids__/F114CA1D-295F-42A5-A5BD-D1A1B19FC049/60E68E32-4C87-4E63-9BF2-12A25E8F2623/ HTTP/1.1" 207 114349 "-" "CalendarStore/5.0.2 (1166); iCal/5.0.2 (1571); Mac OS X/10.7.3 (11D50d)" i=7 or=1 t=764.7 t-req-proc=4.8 t-resp-gen=754.5 t-resp-wr=5.1 t-log=0.2 rcount=2
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twext/web2/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twext/web2/resource.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twext/web2/resource.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -34,6 +34,7 @@
from twext.web2 import iweb, http, server, responsecode
+from twisted.internet.defer import maybeDeferred
class RenderMixin(object):
"""
Mix-in class for L{iweb.IResource} which provides a dispatch mechanism for
@@ -106,9 +107,20 @@
returnValue(response)
yield self.checkPreconditions(request)
- returnValue((yield method(request)))
+ result = maybeDeferred(method, request)
+ result.addErrback(self.methodRaisedException)
+ returnValue((yield result))
+ def methodRaisedException(self, failure):
+ """
+ An C{http_METHOD} method raised an exception; this is an errback for
+ that exception. By default, simply propagate the error up; subclasses
+ may override this for top-level exception handling.
+ """
+ return failure
+
+
def http_OPTIONS(self, request):
"""
Respond to a OPTIONS request.
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcalendar.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcalendar.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -106,7 +106,6 @@
if got_an_error:
# Force a transaction error and proper clean-up
- self.transactionError()
errors.error()
raise HTTPError(MultiStatusResponse([errors.response()]))
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcol.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcol.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/method/mkcol.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -182,7 +182,6 @@
if got_an_error:
# Clean up
- self.transactionError()
errors.error()
raise HTTPError(Response(
code=responsecode.FORBIDDEN,
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/resource.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/resource.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -318,8 +318,14 @@
otherResource.associateWithTransaction(self._associatedTransaction)
- def transactionError(self):
+ def methodRaisedException(self, failure):
+ """
+ An C{http_METHOD} method raised an exception. Any type of exception,
+ including those that result in perfectly valid HTTP responses, should
+ abort the transaction.
+ """
self._transactionError = True
+ return super(CalDAVResource, self).methodRaisedException(failure)
@inlineCallbacks
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/icaldiff.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/icaldiff.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -669,6 +669,7 @@
prop = component.getProperty("X-APPLE-NEEDS-REPLY")
if prop:
component.removeProperty(prop)
+ component.replaceProperty(Property("TRANSP", "TRANSPARENT"))
return partstatChanged
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/imip/mailgateway.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/imip/mailgateway.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/imip/mailgateway.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -1880,5 +1880,5 @@
def clientConnectionFailed(self, connector, reason):
self.connector = connector
- self.log_error("IMAP factory connection failed")
+ self.log_warn("IMAP factory connection failed")
self.retry(connector)
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/implicit.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/implicit.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -812,16 +812,18 @@
# Test/fix ones removed
for rid in valid_old_rids:
- # Compare the old one to the new master
- # Note it is hard to recover from this state so raise instead
- self.compareAttendeePartstats(
+ # Compare the old one to a derived instance, and if there is a change
+ # add the derived instance to the new data
+ newcomp = self.calendar.deriveInstance(rid)
+ changed = self.compareAttendeePartstats(
self.oldcalendar.overriddenComponent(rid),
- self.calendar.overriddenComponent(None),
- raiseOnMisMatch=True
+ newcomp,
)
+ if changed:
+ self.calendar.addComponent(newcomp)
- def compareAttendeePartstats(self, old_component, new_component, raiseOnMisMatch=False):
+ def compareAttendeePartstats(self, old_component, new_component):
"""
Compare two components, old and new, and make sure the Organizer has not changed the PARTSTATs
in the new one to anything other than NEEDS-ACTION. If there is a change, undo it.
@@ -830,6 +832,7 @@
old_attendees = dict([(normalizeCUAddr(attendee.value()), attendee) for attendee in old_component.getAllAttendeeProperties()])
new_attendees = dict([(normalizeCUAddr(attendee.value()), attendee) for attendee in new_component.getAllAttendeeProperties()])
+ changed = False
for cuaddr, newattendee in new_attendees.items():
# Don't adjust ORGANIZER's ATTENDEE
if newattendee.value() in self.organizerPrincipal.calendarUserAddresses():
@@ -839,16 +842,12 @@
old_attendee = old_attendees.get(cuaddr)
old_partstat = old_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper() if old_attendee else "NEEDS-ACTION"
if old_attendee is None or old_partstat != new_partstat:
- if raiseOnMisMatch:
- raise HTTPError(ErrorResponse(
- responsecode.FORBIDDEN,
- (caldav_namespace, "valid-organizer-change"),
- "Organizer cannot change Attendee PARTSTAT",
- ))
- else:
- newattendee.setParameter("PARTSTAT", old_partstat)
+ newattendee.setParameter("PARTSTAT", old_partstat)
+ changed = True
+ return changed
+
@inlineCallbacks
def scheduleWithAttendees(self):
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/test/test_icaldiff.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/test/test_icaldiff.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -1738,6 +1738,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
END:VCALENDAR
@@ -1795,6 +1796,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
BEGIN:VEVENT
@@ -1805,6 +1807,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
END:VCALENDAR
@@ -1871,6 +1874,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
END:VCALENDAR
@@ -1938,6 +1942,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
BEGIN:VEVENT
@@ -1948,6 +1953,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=DECLINED;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
X-CALENDARSERVER-HIDDEN-INSTANCE:T
END:VEVENT
END:VCALENDAR
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/storebridge.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/storebridge.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -1611,8 +1611,6 @@
yield readStream(request.stream, t.write)
except Exception, e:
log.error("Unable to store attachment: %s" % (e,))
- # Signal to abort in twistedcaldav.resource.CalDAVResource.RenderHTTP
- self.transactionError()
raise HTTPError(SERVICE_UNAVAILABLE)
try:
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/test/test_wrapping.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/test/test_wrapping.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -53,6 +53,7 @@
from twistedcaldav.directory.test.test_xmlfile import XMLFileBase
from txdav.caldav.icalendarstore import ICalendarHome
from txdav.carddav.iaddressbookstore import IAddressBookHome
+
from txdav.caldav.datastore.file import Calendar
@@ -108,6 +109,7 @@
@param objectName: The name of a calendar object.
@type objectName: str
+
@param objectText: Some iCalendar text to populate it with.
@type objectText: str
"""
@@ -167,7 +169,6 @@
@param path: the path from the root of the site (not starting with a
slash)
-
@type path: C{str}
@param method: the HTTP method to initialize the request with.
@@ -266,8 +267,8 @@
def test_createStore(self):
"""
- Creating a DirectoryCalendarHomeProvisioningResource will create a paired
- CalendarStore.
+ Creating a DirectoryCalendarHomeProvisioningResource will create a
+ paired CalendarStore.
"""
assertProvides(self, IDataStore, self.calendarCollection._newStore)
@@ -518,7 +519,20 @@
frozenset([self.principalsResource]))
+ @inlineCallbacks
+ def assertCalendarEmpty(self, user, calendarName="calendar"):
+ """
+ Assert that a user's calendar is empty (their default calendar by default).
+ """
+ txn = self.calendarStore.newTransaction()
+ self.addCleanup(txn.commit)
+ home = yield txn.calendarHomeWithUID(user, create=True)
+ cal = yield home.calendarWithName(calendarName)
+ objects = yield cal.calendarObjects()
+ self.assertEquals(len(objects), 0)
+
+
class DatabaseWrappingTests(WrappingTests):
@inlineCallbacks
@@ -531,4 +545,73 @@
return self.calendarStore
+ @inlineCallbacks
+ def test_invalidCalendarPUT(self):
+ """
+ Exceeding quota on an attachment returns an HTTP error code.
+ """
+ # yield self.populateOneObject("1.ics", test_event_text)
+ @inlineCallbacks
+ def putEvt(txt):
+ calendarObject = yield self.getResource(
+ "/calendars/users/wsanchez/calendar/1.ics",
+ "PUT", "wsanchez"
+ )
+ self.requestUnderTest.stream = MemoryStream(txt)
+ returnValue(
+ ((yield calendarObject.renderHTTP(self.requestUnderTest)),
+ self.requestUnderTest)
+ )
+ # see twistedcaldav/directory/test/accounts.xml
+ wsanchez = '6423F94A-6B76-4A3A-815B-D52CFD77935D'
+ cdaboo = '5A985493-EE2C-4665-94CF-4DFEA3A89500'
+ eventTemplate="""\
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+DTSTART:20110101T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+RRULE:FREQ=DAILY;COUNT=2
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+RECURRENCE-ID:20110102T050000Z
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{cdaboo}
+DTSTART:20110102T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+END:VEVENT{0}
+END:VCALENDAR
+"""
+ CR = "\n"
+ CRLF = "\r\n"
+ #validEvent = eventTemplate.format("", wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
+ invalidInstance = """
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+RECURRENCE-ID:20110110T050000Z
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+DTSTART:20110110T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+END:VEVENT""".format(wsanchez=wsanchez, cdaboo=cdaboo)
+ #txn = self.requestUnderTest._newStoreTransaction
+ invalidEvent = eventTemplate.format(invalidInstance, wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
+ resp2, rsrc2 = yield putEvt(invalidEvent)
+ self.requestUnderTest = None
+ yield self.assertCalendarEmpty(wsanchez)
+ yield self.assertCalendarEmpty(cdaboo)
+
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/base/propertystore/sql.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/base/propertystore/sql.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -30,6 +30,8 @@
Select, Parameter, Update, Insert, TableSyntax, Delete)
from txdav.xml.parser import WebDAVDocument
+from txdav.common.icommondatastore import AllRetriesFailed
+from twext.python.log import LoggingMixIn
from txdav.common.datastore.sql_tables import schema
from txdav.base.propertystore.base import (AbstractPropertyStore,
PropertyName, validKey)
@@ -39,7 +41,7 @@
prop = schema.RESOURCE_PROPERTY
-class PropertyStore(AbstractPropertyStore):
+class PropertyStore(AbstractPropertyStore, LoggingMixIn):
_cacher = Memcacher("SQL.props", pickle=True, key_normalization=False)
@@ -255,7 +257,10 @@
if hasattr(self, "_notifyCallback") and self._notifyCallback is not None:
self._notifyCallback()
- self._txn.subtransaction(trySetItem)
+ def justLogIt(f):
+ f.trap(AllRetriesFailed)
+ self.log_error("setting a property failed; probably nothing.")
+ self._txn.subtransaction(trySetItem).addErrback(justLogIt)
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/common.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/common.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -21,7 +21,7 @@
from StringIO import StringIO
-from twisted.internet.defer import Deferred, inlineCallbacks, returnValue,\
+from twisted.internet.defer import Deferred, inlineCallbacks, returnValue, \
maybeDeferred
from twisted.internet.protocol import Protocol
from twisted.python import hashlib
@@ -427,7 +427,7 @@
def test_notificationSyncToken(self):
"""
L{ICalendar.resourceNamesSinceToken} will return the names of calendar
- objects changed or deleted since
+ objects changed or deleted since
"""
txn = self.transactionUnderTest()
coll = yield txn.notificationsWithUID("home1")
@@ -574,12 +574,14 @@
self.assertEquals(calendar.notifierID(), "CalDAV|home1")
self.assertEquals(calendar.notifierID(label="collection"), "CalDAV|home1/calendar_1")
+
@inlineCallbacks
def test_nodeNameSuccess(self):
home = yield self.homeUnderTest()
name = yield home.nodeName()
self.assertEquals(name, "/CalDAV/example.com/home1/")
+
@inlineCallbacks
def test_nodeNameFailure(self):
# The StubNodeCacher is set up to fail when the node name has the
@@ -589,7 +591,66 @@
name = yield home.nodeName()
self.assertEquals(name, None)
+
@inlineCallbacks
+ def test_calendarHomes(self):
+ """
+ Finding all existing calendar homes.
+ """
+ calendarHomes = (yield self.transactionUnderTest().calendarHomes())
+ self.assertEquals(
+ [home.name() for home in calendarHomes],
+ [
+ "home1",
+ "home_no_splits",
+ "home_splits",
+ "home_splits_shared",
+ ]
+ )
+
+
+ @inlineCallbacks
+ def test_displayNameNone(self):
+ """
+ L{ICalendarHome.calendarWithName} returns C{None} for calendars which
+ do not exist.
+ """
+ home = (yield self.homeUnderTest())
+ calendar = (yield home.calendarWithName("calendar_1"))
+ name = (yield calendar.displayName())
+ self.assertEquals(name, None)
+
+
+ @inlineCallbacks
+ def test_setDisplayName(self):
+ """
+ L{ICalendarHome.calendarWithName} returns C{None} for calendars which
+ do not exist.
+ """
+ home = (yield self.homeUnderTest())
+ calendar = (yield home.calendarWithName("calendar_1"))
+
+ calendar.setDisplayName(u"quux")
+ name = calendar.displayName()
+ self.assertEquals(name, u"quux")
+
+ calendar.setDisplayName(None)
+ name = calendar.displayName()
+ self.assertEquals(name, None)
+
+
+ @inlineCallbacks
+ def test_setDisplayNameBytes(self):
+ """
+ L{ICalendarHome.calendarWithName} returns C{None} for calendars which
+ do not exist.
+ """
+ home = (yield self.homeUnderTest())
+ calendar = (yield home.calendarWithName("calendar_1"))
+ self.assertRaises(ValueError, calendar.setDisplayName, "quux")
+
+
+ @inlineCallbacks
def test_calendarHomeWithUID_exists(self):
"""
Finding an existing calendar home by UID results in an object that
@@ -640,6 +701,7 @@
self.assertProvides(ICalendar, calendar)
self.assertEquals(calendar.name(), name)
+
@inlineCallbacks
def test_calendarWithName_exists(self):
"""
@@ -736,7 +798,7 @@
"""
L{ICalendarHome.createCalendarWithName} raises
L{CalendarAlreadyExistsError} when the name conflicts with an already-
- existing
+ existing
"""
home = yield self.homeUnderTest()
for name in home1_calendarNames:
@@ -806,23 +868,25 @@
result = yield maybeDeferred(calendar.getSupportedComponents)
self.assertEquals(result, None)
+
@inlineCallbacks
def test_countComponentTypes(self):
"""
Test Calendar._countComponentTypes to make sure correct counts are returned.
"""
-
+
tests = (
("calendar_1", (("VEVENT", 3),)),
("calendar_2", (("VEVENT", 3), ("VTODO", 2))),
)
-
+
for calname, results in tests:
testalendar = yield (yield self.transactionUnderTest().calendarHomeWithUID(
"home_splits")).calendarWithName(calname)
result = yield maybeDeferred(testalendar._countComponentTypes)
self.assertEquals(result, results)
+
@inlineCallbacks
def test_calendarObjects(self):
"""
@@ -947,6 +1011,7 @@
]
)
+
@inlineCallbacks
def test_removeCalendarObjectWithName_exists(self):
"""
@@ -1044,6 +1109,7 @@
invitedCals = yield cal.asShared()
self.assertEqual(len(invitedCals), 0)
+
@inlineCallbacks
def test_unshareSharerSide(self, commit=False):
"""
@@ -1063,6 +1129,7 @@
invitedCals = yield cal.asShared()
self.assertEqual(len(invitedCals), 0)
+
@inlineCallbacks
def test_unshareShareeSide(self, commit=False):
"""
@@ -1082,6 +1149,7 @@
invitedCals = yield cal.asShared()
self.assertEqual(len(invitedCals), 0)
+
@inlineCallbacks
def test_unshareWithInDifferentTransaction(self):
"""
@@ -1130,7 +1198,7 @@
result = (yield home.hasCalendarResourceUIDSomewhereElse("uid2", object, "schedule"))
self.assertTrue(result)
-
+
# FIXME: do this without legacy calls
'''
from twistedcaldav.sharing import SharedCollectionRecord
@@ -1247,7 +1315,6 @@
self.assertEquals(component.mainType(), "VEVENT")
self.assertEquals(component.resourceUID(), "uid1")
-
perUserComponent = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
VERSION:2.0
@@ -1282,7 +1349,6 @@
END:VCALENDAR
""".replace("\n", "\r\n"))
-
asSeenByOwner = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
VERSION:2.0
@@ -1302,7 +1368,6 @@
END:VCALENDAR
""".replace("\n", "\r\n"))
-
asSeenByOther = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
VERSION:2.0
@@ -1420,6 +1485,7 @@
set(home1_calendarNames)
)
+
@inlineCallbacks
def test_loadAllCalendars(self):
"""
@@ -1441,7 +1507,7 @@
set(c.name() for c in calendars),
set(home1_calendarNames)
)
-
+
for c in calendars:
self.assertTrue(c.properties() is not None)
@@ -1526,6 +1592,7 @@
InvalidObjectResourceError,
)
+
@inlineCallbacks
def test_setComponent_invalid(self):
"""
@@ -1695,7 +1762,7 @@
self.assertNotEquals(event1_text, event1_text_withDifferentSubject)
newComponent = VComponent.fromString(event1_text_withDifferentSubject)
yield obj.setComponent(newComponent)
-
+
# Putting everything into a separate transaction to account for any
# caching that may take place.
yield self.commit()
@@ -1704,7 +1771,6 @@
propertyContent
)
-
eventWithDropbox = "\r\n".join("""
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
@@ -1813,7 +1879,7 @@
def test_collectionSyncToken(self):
"""
L{ICalendar.resourceNamesSinceToken} will return the names of calendar
- objects changed or deleted since
+ objects changed or deleted since
"""
cal = yield self.calendarUnderTest()
st = yield cal.syncToken()
@@ -2037,7 +2103,7 @@
that fails with L{QuotaExceeded}.
"""
home = yield self.homeUnderTest()
- attachment = yield getit()
+ attachment = yield getit()
t = attachment.store(MimeType("text", "x-fixture"))
sample = "all work and no play makes jack a dull boy"
chunk = (sample * (home.quotaAllowedBytes() / len(sample)))
@@ -2097,7 +2163,7 @@
yield checkOriginal()
- def test_removeAttachmentWithName(self, refresh=lambda x:x):
+ def test_removeAttachmentWithName(self, refresh=lambda x: x):
"""
L{ICalendarObject.removeAttachmentWithName} will remove the calendar
object with the given name.
@@ -2152,7 +2218,7 @@
@inlineCallbacks
def test_finishedOnCommit(self):
- """
+ """
Calling L{ITransaction.abort} or L{ITransaction.commit} after
L{ITransaction.commit} has already been called raises an
L{AlreadyFinishedError}.
@@ -2234,4 +2300,3 @@
additionalUIDs.add("home_attachments")
expectedUIDs = additionalUIDs.union(requiredUIDs)
self.assertEquals(foundUIDs, expectedUIDs)
-
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/test_file.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/datastore/test/test_file.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -67,7 +67,7 @@
calendarPath.parent().makedirs()
storePath.copyTo(calendarPath)
- # Set year values to current year
+ # Set year values to current year
nowYear = PyCalendarDateTime.getToday().getYear()
for home in calendarPath.child("ho").child("me").children():
if not home.basename().startswith("."):
@@ -75,7 +75,7 @@
if not calendar.basename().startswith("."):
for resource in calendar.children():
if resource.basename().endswith(".ics"):
- resource.setContent(resource.getContent() % {"now":nowYear})
+ resource.setContent(resource.getContent() % {"now": nowYear})
testID = test.id()
test.calendarStore = CalendarStore(storeRootPath, test.notifierFactory,
@@ -314,8 +314,8 @@
self.calendar1.removeCalendarObjectWithName, name
)
+ counter = 0
- counter = 0
@inlineCallbacks
def _refresh(self):
"""
@@ -442,7 +442,7 @@
L{CalendarObject} has instance attributes, C{_path} and C{_calendar},
which refer to its position in the filesystem and the calendar in which
it is contained, respectively.
- """
+ """
self.failUnless(
isinstance(self.object1._path, FilePath),
self.object1._path
@@ -482,7 +482,6 @@
Overridden to be skipped.
"""
-
# TODO: ideally the file store would support all of this sharing stuff.
test_shareWith.skip = "Not implemented for file store yet."
test_shareAgainChangesMode = test_shareWith
@@ -503,6 +502,25 @@
@inlineCallbacks
+ def test_calendarHomes(self):
+ """
+ Finding all existing calendar homes.
+ """
+ calendarHomes = (yield self.transactionUnderTest().calendarHomes())
+ self.assertEquals(
+ [home.name() for home in calendarHomes],
+ [
+ "home1",
+ "home_attachments",
+ "home_bad",
+ "home_no_splits",
+ "home_splits",
+ "home_splits_shared",
+ ]
+ )
+
+
+ @inlineCallbacks
def test_calendarObjectsWithDotFile(self):
"""
Adding a dotfile to the calendar home should not increase the number of
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/icalendarstore.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/caldav/icalendarstore.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -72,6 +72,7 @@
self.limit = lowerLimit
+
class TimeRangeUpperLimit(Exception):
"""
A request for time-range information too far in the future cannot be satisfied.
@@ -81,11 +82,20 @@
self.limit = upperLimit
+
class ICalendarTransaction(ICommonTransaction):
"""
Transaction functionality required to be implemented by calendar stores.
"""
+ def calendarHomes():
+ """
+ Retrieve each calendar home in the store.
+
+ @return: a L{Deferred} which fires with a list of L{ICalendarHome}.
+ """
+
+
def calendarHomeWithUID(uid, create=False):
"""
Retrieve the calendar home for the principal with the given C{uid}.
@@ -115,6 +125,7 @@
"""
+
#
# Interfaces
#
@@ -134,6 +145,7 @@
@return: a string.
"""
+
def calendars():
"""
Retrieve calendars contained in this calendar home.
@@ -141,6 +153,7 @@
@return: an iterable of L{ICalendar}s.
"""
+
def loadCalendars():
"""
Pre-load all calendars Depth:1.
@@ -148,6 +161,7 @@
@return: an iterable of L{ICalendar}s.
"""
+
def calendarWithName(name):
"""
Retrieve the calendar with the given C{name} contained in this
@@ -185,6 +199,7 @@
given C{name} already exists.
"""
+
def removeCalendarWithName(name):
"""
Remove the calendar with the given C{name} from this calendar
@@ -260,7 +275,20 @@
Change the name of this calendar.
"""
+ def displayName():
+ """
+ Get the display name of this calendar.
+ @return: a unicode string.
+ """
+
+ def setDisplayName(name):
+ """
+ Set the display name of this calendar.
+
+ @param name: a C{unicode}.
+ """
+
def ownerCalendarHome():
"""
Retrieve the calendar home for the owner of this calendar. Calendars
@@ -275,6 +303,7 @@
@return: an L{ICalendarHome}.
"""
+
def calendarObjects():
"""
Retrieve the calendar objects contained in this calendar.
@@ -282,6 +311,7 @@
@return: an iterable of L{ICalendarObject}s.
"""
+
def calendarObjectWithName(name):
"""
Retrieve the calendar object with the given C{name} contained
@@ -292,6 +322,7 @@
object exists.
"""
+
def calendarObjectWithUID(uid):
"""
Retrieve the calendar object with the given C{uid} contained
@@ -303,6 +334,7 @@
such calendar object exists.
"""
+
def createCalendarObjectWithName(name, component):
"""
Create a calendar component with the given C{name} in this
@@ -320,6 +352,7 @@
a calendar object.
"""
+
def removeCalendarObjectWithName(name):
"""
Remove the calendar object with the given C{name} from this
@@ -330,6 +363,7 @@
exists.
"""
+
def removeCalendarObjectWithUID(uid):
"""
Remove the calendar object with the given C{uid} from this
@@ -340,6 +374,7 @@
not exist.
"""
+
def syncToken():
"""
Retrieve the current sync token for this calendar.
@@ -347,6 +382,7 @@
@return: a string containing a sync token.
"""
+
def calendarObjectsInTimeRange(start, end, timeZone):
"""
Retrieve all calendar objects in this calendar which have
@@ -359,6 +395,7 @@
@return: an iterable of L{ICalendarObject}s.
"""
+
def calendarObjectsSinceToken(token):
"""
Retrieve all calendar objects in this calendar that have
@@ -417,6 +454,8 @@
"""
# TODO: implement this for the file store.
+
+
class ICalendarObject(IDataStoreObject):
"""
Calendar object
@@ -431,6 +470,7 @@
@rtype: L{ICalendar}
"""
+
def setComponent(component):
"""
Rewrite this calendar object to match the given C{component}.
@@ -443,6 +483,7 @@
a calendar object.
"""
+
def component():
"""
Retrieve the calendar component for this calendar object.
@@ -454,6 +495,7 @@
@return: a C{VCALENDAR} L{VComponent}.
"""
+
def uid():
"""
Retrieve the UID for this calendar object.
@@ -461,6 +503,7 @@
@return: a string containing a UID.
"""
+
def componentType():
"""
Retrieve the iCalendar component type for the main component
@@ -484,6 +527,7 @@
@return: a URI string.
"""
+
def dropboxID():
"""
An identifier, unique to the calendar home, that specifies a location
@@ -616,5 +660,3 @@
that the stream is complete to its C{connectionLost} method.
@type protocol: L{IProtocol}
"""
-
-
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/common.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/common.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -234,6 +234,20 @@
@inlineCallbacks
+ def test_addressbookHomes(self):
+ """
+ Finding all existing addressbook homes.
+ """
+ addressbookHomes = (yield self.transactionUnderTest().addressbookHomes())
+ self.assertEquals(
+ [home.name() for home in addressbookHomes],
+ [
+ "home1",
+ ]
+ )
+
+
+ @inlineCallbacks
def test_addressbookHomeWithUID_exists(self):
"""
Finding an existing addressbook home by UID results in an object that
@@ -664,7 +678,7 @@
set(c.name() for c in addressbooks),
set(home1_addressbookNames)
)
-
+
for c in addressbooks:
self.assertTrue(c.properties() is not None)
@@ -910,7 +924,7 @@
self.assertNotEquals(vcard1_text, vcard1_text_withDifferentNote)
newComponent = VComponent.fromString(vcard1_text_withDifferentNote)
yield obj.setComponent(newComponent)
-
+
# Putting everything into a separate transaction to account for any
# caching that may take place.
yield self.commit()
@@ -979,6 +993,3 @@
additionalUIDs.add("home_bad")
expectedUIDs = additionalUIDs.union(requiredUIDs)
self.assertEquals(foundUIDs, expectedUIDs)
-
-
-
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/test_file.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/datastore/test/test_file.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -434,7 +434,7 @@
L{AddressBookObject} has instance attributes, C{_path} and C{_addressbook},
which refer to its position in the filesystem and the addressbook in which
it is contained, respectively.
- """
+ """
self.failUnless(
isinstance(self.object1._path, FilePath),
self.object1._path
@@ -445,6 +445,7 @@
)
+
class FileStorageTests(CommonTests, unittest.TestCase):
"""
File storage tests.
@@ -471,6 +472,21 @@
@inlineCallbacks
+ def test_addressbookHomes(self):
+ """
+ Finding all existing addressbook homes.
+ """
+ addressbookHomes = (yield self.transactionUnderTest().addressbookHomes())
+ self.assertEquals(
+ [home.name() for home in addressbookHomes],
+ [
+ "home1",
+ "home_bad",
+ ]
+ )
+
+
+ @inlineCallbacks
def test_addressbookObjectsWithDotFile(self):
"""
Adding a dotfile to the addressbook home should not create a new
@@ -489,5 +505,3 @@
((yield self.addressbookUnderTest())._path.child("not-a-vcard")
.createDirectory())
yield self.test_addressbookObjects()
-
-
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/iaddressbookstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/iaddressbookstore.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/carddav/iaddressbookstore.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -19,7 +19,7 @@
Address book store interfaces
"""
-from txdav.common.icommondatastore import ICommonTransaction,\
+from txdav.common.icommondatastore import ICommonTransaction, \
IShareableCollection
from txdav.idav import INotifier
from txdav.idav import IDataStoreObject
@@ -37,6 +37,14 @@
Transaction interface that addressbook stores must provide.
"""
+ def addressbookHomes():
+ """
+ Retrieve each addressbook home in the store.
+
+ @return: a L{Deferred} which fires with a list of L{ICalendarHome}.
+ """
+
+
def addressbookHomeWithUID(uid, create=False):
"""
Retrieve the addressbook home for the principal with the given C{uid}.
@@ -79,6 +87,7 @@
@return: an iterable of L{IAddressBook}s.
"""
+
def loadAddressbooks():
"""
Pre-load all addressbooks Depth:1.
@@ -86,6 +95,7 @@
@return: an iterable of L{IAddressBook}s.
"""
+
def addressbookWithName(name):
"""
Retrieve the addressbook with the given C{name} contained in this
@@ -96,6 +106,7 @@
exists.
"""
+
def createAddressBookWithName(name):
"""
Create an addressbook with the given C{name} in this addressbook
@@ -106,6 +117,7 @@
given C{name} already exists.
"""
+
def removeAddressBookWithName(name):
"""
Remove the addressbook with the given C{name} from this addressbook
@@ -117,6 +129,7 @@
"""
+
class IAddressBook(INotifier, IShareableCollection, IDataStoreObject):
"""
AddressBook
@@ -132,6 +145,7 @@
Change the name of this addressbook.
"""
+
def ownerAddressBookHome():
"""
Retrieve the addressbook home for the owner of this addressbook.
@@ -141,6 +155,7 @@
@return: an L{IAddressBookHome}.
"""
+
def addressbookObjects():
"""
Retrieve the addressbook objects contained in this addressbook.
@@ -148,13 +163,14 @@
@return: an iterable of L{IAddressBookObject}s.
"""
+
def addressbookObjectWithName(name):
"""
Retrieve the addressbook object with the given C{name} contained
in this addressbook.
@param name: a string.
-
+
@return: a L{Deferred} that fires with an L{IAddressBookObject} or
C{None} if no such addressbook object exists.
"""
@@ -169,6 +185,7 @@
object exists.
"""
+
def createAddressBookObjectWithName(name, component):
"""
Create an addressbook component with the given C{name} in this
@@ -186,6 +203,7 @@
an addressbook object.
"""
+
def removeAddressBookObjectWithName(name):
"""
Remove the addressbook object with the given C{name} from this
@@ -196,6 +214,7 @@
exists.
"""
+
def removeAddressBookObjectWithUID(uid):
"""
Remove the addressbook object with the given C{uid} from this
@@ -206,6 +225,7 @@
not exist.
"""
+
def syncToken():
"""
Retrieve the current sync token for this addressbook.
@@ -213,6 +233,7 @@
@return: a string containing a sync token.
"""
+
def addressbookObjectsSinceToken(token):
"""
Retrieve all addressbook objects in this addressbook that have
@@ -225,6 +246,7 @@
"""
+
class IAddressBookObject(IDataStoreObject):
"""
AddressBook object
@@ -250,6 +272,7 @@
an addressbook object.
"""
+
def component():
"""
Retrieve the addressbook component for this addressbook object.
@@ -261,6 +284,7 @@
@return: a C{VCARD} L{VComponent}.
"""
+
def uid():
"""
Retrieve the UID for this addressbook object.
Copied: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/common.py (from rev 9978, CalendarServer/trunk/txdav/common/datastore/common.py)
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/common.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/common.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -0,0 +1,55 @@
+# -*- test-case-name: txdav -*-
+##
+# Copyright (c) 2012 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.
+##
+
+"""
+Common functionality that is the same for different data store
+implementations.
+"""
+
+__all__ = [
+]
+
+
+from txdav.xml.element import DisplayName
+from txdav.base.propertystore.base import PropertyName
+
+
+class HomeChildBase(object):
+ """
+ Home child (address book or calendar) common functionality.
+ """
+
+ def displayName(self):
+ name = self.properties().get(PropertyName.fromElement(DisplayName), None)
+ if name is None:
+ return None
+ else:
+ return name.toString()
+
+
+ def setDisplayName(self, name):
+ if name is None:
+ del self.properties()[PropertyName.fromElement(DisplayName)]
+ else:
+ if not isinstance(name, unicode):
+ raise ValueError("Display name must be unicode: %r" % (name,))
+
+ self.properties()[
+ PropertyName.fromElement(DisplayName)
+ ] = DisplayName.fromString(name)
+
+ return None
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/file.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/file.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -24,7 +24,7 @@
from txdav.xml.rfc2518 import ResourceType, GETContentType, HRef
from txdav.xml.rfc5842 import ResourceID
from twext.web2.http_headers import generateContentType, MimeType
-from twext.web2.dav.resource import TwistedGETContentMD5,\
+from twext.web2.dav.resource import TwistedGETContentMD5, \
TwistedQuotaUsedProperty
from twisted.internet.defer import succeed, inlineCallbacks, returnValue
@@ -38,6 +38,7 @@
from twistedcaldav.sharing import SharedCollectionsDatabase
from txdav.caldav.icalendarstore import ICalendarStore, BIND_OWN
+from txdav.common.datastore.common import HomeChildBase
from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
HomeChildNameAlreadyExistsError, NoSuchHomeChildError, \
InternalDataStoreError, ObjectResourceNameNotAllowedError, \
@@ -138,6 +139,7 @@
self._migrating = state
self._enableNotifications = not state
+
def setUpgrading(self, state):
"""
Set the "upgrading" state
@@ -178,6 +180,7 @@
return self._homesOfType(EADDRESSBOOKTYPE)
+
class CommonStoreTransaction(DataStoreTransaction):
"""
In-memory implementation of
@@ -225,14 +228,45 @@
CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = AddressBookHome
- @memoizedKey('uid', '_calendarHomes')
+ def calendarHomes(self):
+ return self.homes(ECALENDARTYPE)
+
+
def calendarHomeWithUID(self, uid, create=False):
return self.homeWithUID(ECALENDARTYPE, uid, create=create)
- @memoizedKey("uid", "_addressbookHomes")
+
+ def addressbookHomes(self):
+ return self.homes(EADDRESSBOOKTYPE)
+
+
def addressbookHomeWithUID(self, uid, create=False):
return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
+
+ def _determineMemo(self, storeType, uid, create=False):
+ """
+ Determine the memo dictionary to use for homeWithUID.
+ """
+ if storeType == ECALENDARTYPE:
+ return self._calendarHomes
+ else:
+ return self._addressbookHomes
+
+
+ def homes(self, storeType):
+ """
+ Load all calendar or addressbook homes.
+ """
+ uids = self._homeClass[storeType].listHomes(self)
+ for uid in uids:
+ self.homeWithUID(storeType, uid, create=False)
+
+ # Return the memoized list directly
+ returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, None).items(), key=lambda x: x[0])])
+
+
+ @memoizedKey("uid", _determineMemo)
def homeWithUID(self, storeType, uid, create=False):
if uid.startswith("."):
return None
@@ -242,6 +276,7 @@
return self._homeClass[storeType].homeWithUID(self, uid, create, storeType == ECALENDARTYPE)
+
@memoizedKey("uid", "_notificationHomes")
def notificationsWithUID(self, uid, home=None):
@@ -254,27 +289,35 @@
def addAPNSubscription(self, token, key, timestamp, subscriber, userAgent, ipAddr):
return NotImplementedError
+
def removeAPNSubscription(self, token, key):
return NotImplementedError
+
def purgeOldAPNSubscriptions(self, purgeSeconds):
return NotImplementedError
+
def apnSubscriptionsByToken(self, token):
return NotImplementedError
+
def apnSubscriptionsByKey(self, key):
return NotImplementedError
+
def apnSubscriptionsBySubscriber(self, guid):
return NotImplementedError
+
def isNotifiedAlready(self, obj):
return obj in self._notifiedAlready
+
def notificationAddedForObject(self, obj):
self._notifiedAlready.add(obj)
+
def isBumpedAlready(self, obj):
"""
Indicates whether or not bumpAddedForObject has already been
@@ -283,6 +326,7 @@
"""
return obj in self._bumpedAlready
+
def bumpAddedForObject(self, obj):
"""
Records the fact that a bumpModified( ) call has already been
@@ -331,6 +375,31 @@
@classmethod
+ def listHomes(cls, txn):
+ """
+ Retrieve the owner UIDs of all existing homes.
+
+ @return: an iterable of C{str}s.
+ """
+ results = []
+ top = txn._dataStore._path.child(cls._topPath)
+ if top.exists() and top.isdir() and top.child(UIDPATH).exists():
+ for firstPrefix in top.child(UIDPATH).children():
+ if not isValidName(firstPrefix.basename()):
+ continue
+ for secondPrefix in firstPrefix.children():
+ if not isValidName(secondPrefix.basename()):
+ continue
+ for actualHome in secondPrefix.children():
+ uid = actualHome.basename()
+ if not isValidName(uid):
+ continue
+ results.append(uid)
+
+ return results
+
+
+ @classmethod
def homeWithUID(cls, txn, uid, create=False, withNotifications=False):
assert len(uid) >= 4
@@ -390,6 +459,7 @@
return home
+
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._path)
@@ -401,6 +471,7 @@
def transaction(self):
return self._transaction
+
def retrieveOldShares(self):
"""
Retrieve the old Index object.
@@ -433,7 +504,7 @@
) | set(
name
for name in self._path.listdir()
- if not name.startswith(".") and
+ if not name.startswith(".") and
self._path.child(name).isdir() and
name not in self._removedChildren
))
@@ -515,6 +586,7 @@
self.notifyChanged()
return c
+
@writeOperation
def removeChildWithName(self, name):
if name.startswith(".") or name in self._removedChildren:
@@ -532,13 +604,14 @@
else:
self._removedChildren.add(name)
+
@inlineCallbacks
def syncToken(self):
-
+
maxrev = 0
for child in self.children():
maxrev = max(int((yield child.syncToken()).split("_")[1]), maxrev)
-
+
try:
urnuuid = str(self.properties()[PropertyName.fromElement(ResourceID)].children[0])
except KeyError:
@@ -564,6 +637,7 @@
self._transaction.addOperation(props.flush, "flush home properties")
return props
+
def objectResourcesWithUID(self, uid, ignore_children=()):
"""
Return all child object resources with the specified UID, ignoring any in the
@@ -578,6 +652,7 @@
results.append(object)
return results
+
def quotaUsedBytes(self):
try:
@@ -585,31 +660,34 @@
except KeyError:
return 0
+
def adjustQuotaUsedBytes(self, delta):
"""
Adjust quota used. We need to get a lock on the row first so that the adjustment
is done atomically.
"""
-
+
old_used = self.quotaUsedBytes()
new_used = old_used + delta
if new_used < 0:
self.log_error("Fixing quota adjusted below zero to %s by change amount %s" % (new_used, delta,))
new_used = 0
self.properties()[PropertyName.fromElement(TwistedQuotaUsedProperty)] = TwistedQuotaUsedProperty(str(new_used))
-
+
def addNotifier(self, notifier):
if self._notifiers is None:
self._notifiers = ()
self._notifiers += (notifier,)
-
+
+
def notifierID(self, label="default"):
if self._notifiers:
return self._notifiers[0].getID(label)
else:
return None
+
@inlineCallbacks
def nodeName(self, label="default"):
if self._notifiers:
@@ -620,6 +698,7 @@
else:
returnValue(None)
+
def notifyChanged(self):
"""
Trigger a notification of a change
@@ -632,7 +711,8 @@
self._transaction.notificationAddedForObject(self)
-class CommonHomeChild(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
+
+class CommonHomeChild(FileMetaDataMixin, LoggingMixIn, FancyEqMixin, HomeChildBase):
"""
Common ancestor class of AddressBooks and Calendars.
"""
@@ -683,6 +763,7 @@
def objectWithName(cls, home, name, owned):
return cls(name, home, owned) if home._path.child(name).isdir() else None
+
@property
def _path(self):
return self._home._path.child(self._name)
@@ -691,12 +772,14 @@
def resourceType(self):
return NotImplementedError
+
def retrieveOldIndex(self):
"""
Retrieve the old Index object.
"""
return self._index._oldIndex
+
def retrieveOldInvites(self):
"""
Retrieve the old Invites DB object.
@@ -721,6 +804,7 @@
"""
return BIND_OWN
+
def owned(self):
return self._owned
@@ -742,6 +826,7 @@
self.notifyChanged()
+
@writeOperation
def remove(self):
@@ -855,7 +940,7 @@
rname = self.retrieveOldIndex().resourceNameForUID(uid)
if rname and rname not in self._removedObjectResources:
return self.objectResourceWithName(rname)
-
+
return None
@@ -863,7 +948,7 @@
def createObjectResourceWithName(self, name, component, metadata=None):
"""
Create a new resource with component data and optional metadata. We create the
- python object using the metadata then create the actual store object with setComponent.
+ python object using the metadata then create the actual store object with setComponent.
"""
if name.startswith("."):
raise ObjectResourceNameNotAllowedError(name)
@@ -934,6 +1019,7 @@
"""
return True
+
# FIXME: property writes should be a write operation
@cached
def properties(self):
@@ -957,17 +1043,20 @@
"""
pass
+
def addNotifier(self, notifier):
if self._notifiers is None:
self._notifiers = ()
self._notifiers += (notifier,)
-
+
+
def notifierID(self, label="default"):
if self._notifiers:
return self._notifiers[0].getID(label)
else:
return None
+
@inlineCallbacks
def nodeName(self, label="default"):
if self._notifiers:
@@ -978,6 +1067,7 @@
else:
returnValue(None)
+
def notifyChanged(self):
"""
Trigger a notification of a change
@@ -990,6 +1080,7 @@
self._transaction.notificationAddedForObject(self)
+
class CommonObjectResource(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
"""
@ivar _path: The path of the file on disk
@@ -1017,9 +1108,11 @@
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._path.path)
+
def transaction(self):
return self._transaction
+
@writeOperation
def setComponent(self, component, inserting=False):
raise NotImplementedError
@@ -1036,6 +1129,7 @@
def uid(self):
raise NotImplementedError
+
@cached
def properties(self):
home = self._parentCollection._home
@@ -1049,6 +1143,7 @@
self._transaction.addOperation(props.flush, "object properties flush")
return props
+
def initPropertyStore(self, props):
"""
A hook for subclasses to override in order to set up their property
@@ -1058,6 +1153,8 @@
"""
pass
+
+
class CommonStubResource(object):
"""
Just enough resource to keep the collection sql DB classes going.
@@ -1066,6 +1163,7 @@
self.resource = resource
self.fp = self.resource._path
+
def bumpSyncToken(self, reset=False):
# FIXME: needs direct tests
return self.resource._updateSyncToken(reset)
@@ -1076,6 +1174,7 @@
self.bumpSyncToken(True)
+
class NotificationCollection(CommonHomeChild):
"""
File-based implementation of L{INotificationCollection}.
@@ -1100,6 +1199,7 @@
self._invites = None
self._objectResourceClass = NotificationObject
+
@classmethod
def notificationsFromHome(cls, txn, home):
@@ -1143,6 +1243,7 @@
props[PropertyName(*ResourceType.qname())] = c.resourceType()
return c
+
def resourceType(self):
return ResourceType.notification #@UndefinedVariable
@@ -1155,6 +1256,7 @@
name = uid + ".xml"
return self.notificationObjectWithName(name)
+
def writeNotificationObject(self, uid, xmltype, xmldata):
name = uid + ".xml"
if name.startswith("."):
@@ -1166,9 +1268,10 @@
# Update database
self.retrieveOldIndex().addOrUpdateRecord(NotificationRecord(uid, name, xmltype.name))
-
+
self.notifyChanged()
+
@writeOperation
def removeNotificationObjectWithName(self, name):
if name.startswith("."):
@@ -1185,17 +1288,19 @@
return lambda: None
self._transaction.addOperation(do, "remove object resource object %r" %
(name,))
-
+
self.notifyChanged()
else:
raise NoSuchObjectResourceError(name)
+
@writeOperation
def removeNotificationObjectWithUID(self, uid):
name = uid + ".xml"
self.removeNotificationObjectWithName(name)
+
class NotificationObject(CommonObjectResource):
"""
"""
@@ -1205,6 +1310,7 @@
super(NotificationObject, self).__init__(name, notifications)
self._uid = name[:-4]
+
def notificationCollection(self):
return self._parentCollection
@@ -1215,6 +1321,7 @@
return int(reactor.seconds())
return super(NotificationObject, self).created()
+
def modified(self):
if not self._path.exists():
from twisted.internet import reactor
@@ -1260,11 +1367,10 @@
self.properties().update(self.properties())
props = self.properties()
- props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"})))
+ props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset": "utf-8"})))
props[PropertyName.fromElement(NotificationType)] = NotificationType(xmltype)
props[PropertyName.fromElement(TwistedGETContentMD5)] = TwistedGETContentMD5.fromString(md5)
-
# FIXME: the property store's flush() method may already have been
# added to the transaction, but we need to add it again to make sure it
# happens _after_ the new file has been written. we may end up doing
@@ -1272,7 +1378,6 @@
# manipulation methods won't work.
self._transaction.addOperation(self.properties().flush, "post-update property flush")
-
_xmldata = None
def xmldata(self):
@@ -1297,10 +1402,12 @@
def uid(self):
return self._uid
+
def xmlType(self):
# NB This is the NotificationType property element
return self.properties()[PropertyName.fromElement(NotificationType)]
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
@@ -1312,6 +1419,7 @@
)
+
class NotificationIndex(object):
#
# OK, here's where we get ugly.
@@ -1321,4 +1429,3 @@
self.notificationCollection = notificationCollection
stubResource = CommonStubResource(notificationCollection)
self._oldIndex = OldNotificationIndex(stubResource)
-
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -31,7 +31,8 @@
from twext.python.log import Logger, LoggingMixIn
from twisted.python.log import msg as log_msg, err as log_err
-from txdav.xml.rfc2518 import ResourceType
+
+from txdav.xml.element import ResourceType
from txdav.xml.parser import WebDAVDocument
from twext.web2.http_headers import MimeType
@@ -53,6 +54,7 @@
from txdav.carddav.iaddressbookstore import IAddressBookTransaction
+from txdav.common.datastore.common import HomeChildBase
from txdav.common.datastore.sql_tables import schema
from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, \
_BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED, \
@@ -463,10 +465,18 @@
raise RuntimeError("Database key %s cannot be determined." % (key,))
+ def calendarHomes(self):
+ return self.homes(ECALENDARTYPE)
+
+
def calendarHomeWithUID(self, uid, create=False):
return self.homeWithUID(ECALENDARTYPE, uid, create=create)
+ def addressbookHomes(self):
+ return self.homes(EADDRESSBOOKTYPE)
+
+
def addressbookHomeWithUID(self, uid, create=False):
return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
@@ -481,6 +491,21 @@
return self._addressbookHomes
+ @inlineCallbacks
+ def homes(self, storeType):
+ """
+ Load all calendar or addressbook homes.
+ """
+
+ # Get all UIDs and load them - this will memoize all existing ones
+ uids = (yield self._homeClass[storeType].listHomes(self))
+ for uid in uids:
+ yield self.homeWithUID(storeType, uid, create=False)
+
+ # Return the memoized list directly
+ returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, None).items(), key=lambda x: x[0])])
+
+
@memoizedKey("uid", _determineMemo)
def homeWithUID(self, storeType, uid, create=False):
if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
@@ -726,6 +751,13 @@
block = self._sqlTxn.commandBlock()
sp = self._savepoint()
failuresToMaybeLog = []
+ def end():
+ block.end()
+ for f in failuresToMaybeLog:
+ # TODO: direct tests, to make sure error logging
+ # happens correctly in all cases.
+ log.err(f)
+ raise AllRetriesFailed()
triesLeft = retries
try:
while True:
@@ -733,8 +765,9 @@
try:
result = yield thunk(block)
except:
+ f = Failure()
if not failureOK:
- failuresToMaybeLog.append(Failure())
+ failuresToMaybeLog.append(f)
yield sp.rollback(block)
if triesLeft:
triesLeft -= 1
@@ -749,12 +782,7 @@
block = newBlock
sp = self._savepoint()
else:
- block.end()
- for f in failuresToMaybeLog:
- # TODO: direct tests, to make sure error logging
- # happens correctly in all cases.
- log.err(f)
- raise AllRetriesFailed()
+ end()
else:
yield sp.release(block)
block.end()
@@ -765,9 +793,10 @@
# and only that case - acquire() or release() or commandBlock() may
# raise an AlreadyFinishedError (either synchronously, or in the
# case of the first two, possibly asynchronously as well). We can
- # safely ignore this, because it can't have any real effect; our
- # caller shouldn't be paying attention anyway.
- block.end()
+ # safely ignore this error, because it can't have any effect on what
+ # gets written; our caller will just get told that it failed in a
+ # way they have to be prepared for anyway.
+ end()
@inlineCallbacks
@@ -1101,6 +1130,22 @@
@classmethod
@inlineCallbacks
+ def listHomes(cls, txn):
+ """
+ Retrieve the owner UIDs of all existing homes.
+
+ @return: an iterable of C{str}s.
+ """
+ rows = yield Select(
+ [cls._homeSchema.OWNER_UID],
+ From=cls._homeSchema,
+ ).on(txn)
+ rids = [row[0] for row in rows]
+ returnValue(rids)
+
+
+ @classmethod
+ @inlineCallbacks
def homeWithUID(cls, txn, uid, create=False):
if txn._notifierFactory:
notifiers = (txn._notifierFactory.newNotifier(
@@ -1982,7 +2027,7 @@
-class CommonHomeChild(LoggingMixIn, FancyEqMixin, _SharedSyncLogic):
+class CommonHomeChild(LoggingMixIn, FancyEqMixin, _SharedSyncLogic, HomeChildBase):
"""
Common ancestor class of AddressBooks and Calendars.
"""
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/test/test_sql.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/test/test_sql.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -24,6 +24,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.task import Clock
from twisted.trial.unittest import TestCase
+from twisted.internet.defer import Deferred
from txdav.common.datastore.sql import log, CommonStoreTransactionMonitor,\
CommonHome, CommonHomeChild, ECALENDARTYPE
@@ -227,7 +228,8 @@
@inlineCallbacks
def test_subtransactionFailSomeRetries(self):
"""
- txn.subtransaction runs loop three times when all fail and two retries requested.
+ txn.subtransaction runs loop three times when all fail and two retries
+ requested.
"""
txn = self.transactionUnderTest()
@@ -251,7 +253,35 @@
self.fail("AllRetriesFailed not raised")
self.assertEqual(ctr[0], 3)
+
@inlineCallbacks
+ def test_subtransactionAbortOuterTransaction(self):
+ """
+ If an outer transaction that is holding a subtransaction open is
+ aborted, then the L{Deferred} returned by L{subtransaction} raises
+ L{AllRetriesFailed}.
+ """
+ txn = self.transactionUnderTest()
+ cs = schema.CALENDARSERVER
+ waitAMoment = Deferred()
+ @inlineCallbacks
+ def later(subtxn):
+ yield waitAMoment
+ value = yield Select([cs.VALUE], From=cs).on(subtxn)
+ returnValue(value)
+ started = txn.subtransaction(later)
+ txn.abort()
+ waitAMoment.callback(True)
+ try:
+ result = yield started
+ except AllRetriesFailed:
+ pass
+ else:
+ self.fail("AllRetriesFailed not raised, %r returned instead" %
+ (result,))
+
+
+ @inlineCallbacks
def test_changeRevision(self):
"""
CommonHomeChild._changeRevision actions.
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/__init__.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/__init__.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -52,4 +52,6 @@
import txdav.xml.rfc6578
import txdav.xml.extensions
+# FIXME: add symbols to __all__
+
txdav # Shhh pyflakes
Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/base.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/base.py 2012-10-24 19:10:28 UTC (rev 9978)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/xml/base.py 2012-10-24 19:57:08 UTC (rev 9979)
@@ -589,15 +589,24 @@
def fromString(clazz, string):
if string is None:
return clazz()
- elif isinstance(string, (str, unicode)):
+ elif isinstance(string, (unicode, str)):
return clazz(PCDATAElement(string))
else:
return clazz(PCDATAElement(str(string)))
allowed_children = { PCDATAElement: (0, None) }
+ def toString(self):
+ """
+ @return: a unicode string containing the text in this element.
+ """
+ return self.__str__().decode("utf-8")
+
def __str__(self):
- return "".join([c.data for c in self.children])
+ """
+ @return: a byte string containing the text in this element.
+ """
+ return b"".join([c.data for c in self.children])
def __repr__(self):
content = str(self)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121024/93e13a51/attachment-0001.html>
More information about the calendarserver-changes
mailing list