[CalendarServer-changes] [12110] CalendarServer/branches/users/gaya/sharedgroupfixes
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 12 11:22:44 PDT 2014
Revision: 12110
http://trac.calendarserver.org//changeset/12110
Author: gaya at apple.com
Date: 2013-12-13 22:28:16 -0800 (Fri, 13 Dec 2013)
Log Message:
-----------
merge in 11861 to 12016 from trunk
Modified Paths:
--------------
CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt
CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics
CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics
CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd
CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py
CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py
CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING
CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py
CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py
CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py
CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh
CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py
CalendarServer/branches/users/gaya/sharedgroupfixes/test
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py
Added Paths:
-----------
CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/podding/
CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/polls/
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests-pod/
CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial
CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd
CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/webpoll/
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/application/
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql
Removed Paths:
-------------
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml
CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd
CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql
Property Changed:
----------------
CalDAVTester/branches/users/gaya/sharedgroupfixestester/
CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/
CalendarServer/branches/users/gaya/sharedgroupfixes/
Property changes on: CalDAVTester/branches/users/gaya/sharedgroupfixestester
___________________________________________________________________
Modified: svn:mergeinfo
- /CalDAVTester/branches/release/CalDAVTester-3.0-dev:7584
/CalDAVTester/branches/release/CalDAVTester-4.3-dev:10193
/CalDAVTester/branches/users/cdaboo/attendee-comments-2887:2888-2910
/CalDAVTester/branches/users/cdaboo/better-proxy-3148:3149-3163
/CalDAVTester/branches/users/cdaboo/component-set-fixes:8221-8346
/CalDAVTester/branches/users/cdaboo/conditional-4466:4467-4469
/CalDAVTester/branches/users/cdaboo/implicitauto-2948:2949-2989
/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574:3575-3581
/CalDAVTester/branches/users/cdaboo/managed-attachments:9986-10145
/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533:3534-3558
/CalDAVTester/branches/users/cdaboo/pycalendar:7160-7206
/CalDAVTester/branches/users/cdaboo/pycard:7226-7237
/CalDAVTester/branches/users/cdaboo/sharing-5228:5229-5440
/CalDAVTester/branches/users/gaya/sharedgroupstester-3:11181-11204
/CalDAVTester/trunk:11742-11861
+ /CalDAVTester/branches/release/CalDAVTester-3.0-dev:7584
/CalDAVTester/branches/release/CalDAVTester-4.3-dev:10193
/CalDAVTester/branches/users/cdaboo/attendee-comments-2887:2888-2910
/CalDAVTester/branches/users/cdaboo/better-proxy-3148:3149-3163
/CalDAVTester/branches/users/cdaboo/component-set-fixes:8221-8346
/CalDAVTester/branches/users/cdaboo/conditional-4466:4467-4469
/CalDAVTester/branches/users/cdaboo/implicitauto-2948:2949-2989
/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574:3575-3581
/CalDAVTester/branches/users/cdaboo/managed-attachments:9986-10145
/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533:3534-3558
/CalDAVTester/branches/users/cdaboo/pycalendar:7160-7206
/CalDAVTester/branches/users/cdaboo/pycard:7226-7237
/CalDAVTester/branches/users/cdaboo/sharing-5228:5229-5440
/CalDAVTester/branches/users/gaya/sharedgroupstester-3:11181-11204
/CalDAVTester/trunk:11742-12016
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt 2013-12-14 06:28:16 UTC (rev 12110)
@@ -791,6 +791,7 @@
[+text] - node text starts with "text".
[^tag] - node has child element "tag".
[^tag=text] - node has child element "tag" with text "text".
+ [|] - node is empty.
[json] - node contains valid JSON data.
[icalendar] - node contains valid iCalendare data.
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,7 +1,7 @@
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+PRODID:-//Example Inc.//Example Calendar//EN
BEGIN:VTIMEZONE
TZID:US/Eastern
LAST-MODIFIED:20040110T032845Z
@@ -25,11 +25,10 @@
DTSTART;TZID=US/Eastern:$now.year.1:0101T100000
DURATION:PT1H
ATTENDEE;CN=$username1:;PARTSTAT=ACCEPTED;EMAIL=$email1::$cuaddrurn1:
-ATTENDEE;CN=$username2:;PARTSTAT=ACCEPTED;EMAIL=$email2::$cuaddrurn2:
+ATTENDEE;CN=$username2:;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0;EMAIL=$email2::$cuaddrurn2:
CREATED:20060101T150000Z
DTSTAMP:20051222T205953Z
-ORGANIZER;CN=$username1:;SCHEDULE-STATUS=1.2;EMAIL=$email1::$cuaddrurn1:
+ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
SUMMARY:event 3
-X-CALENDARSERVER-PRIVATE-COMMENT:I have accepted.
END:VEVENT
END:VCALENDAR
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,6 +31,6 @@
ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
SUMMARY:event 3
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="$cuaddrurn2:
- ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:
+ ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:I have accepted.
END:VEVENT
END:VCALENDAR
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,6 +31,6 @@
ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
SUMMARY:event 3
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="$cuaddrurn2:
- ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:
+ ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:I have accepted.
END:VEVENT
END:VCALENDAR
Deleted: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,896 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-
-<!DOCTYPE serverinfo SYSTEM "serverinfo.dtd">
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<serverinfo>
- <host>localhost</host>
- <nonsslport>8008</nonsslport>
- <sslport>8443</sslport>
- <authtype>basic</authtype>
-
- <features>
- <!-- Generic WebDAV extensions -->
- <feature>COPY Method</feature> <!-- COPY method -->
- <feature>MOVE Method</feature> <!-- MOVE method -->
- <feature>Extended MKCOL</feature> <!-- Extended MKCOL -->
-
- <!-- ACL related -->
- <feature>ACL Method</feature> <!-- ACL method -->
- <feature>acl-principal-prop-set REPORT</feature> <!-- ACL acl-principal-prop-set REPORT -->
- <feature>principal-match REPORT</feature> <!-- ACL principal-match REPORT -->
- <feature>principal-property-search REPORT</feature> <!-- ACL principal-property-search REPORT -->
- <feature>principal-search-property-set REPORT</feature> <!-- ACL principal-search-property-set REPORT -->
-
- <feature>add-member</feature> <!-- Add-member used to create resources -->
- <!-- <feature>auth-on-root</feature> --> <!-- Whether the server requires authentication on the root URI -->
- <feature>brief</feature> <!-- Brief header for PROPFIND, REPORT -->
- <feature>bulk-post</feature> <!-- Bulk POST requests -->
- <feature>ctag</feature> <!-- ctag extension -->
- <feature>current-user-principal</feature> <!-- current-user-principal extension -->
- <feature>directory listing</feature> <!-- GET on collection -->
- <feature>extended-principal-search</feature> <!-- Extended principal-property-search REPORT extension -->
- <feature>expand-property</feature> <!-- Expand property REPORT -->
- <feature>only-proxy-groups</feature> <!-- Group-membership only includes delegated-to groups -->
- <feature>limits</feature> <!-- max-collections and max-resources limits -->
- <feature>prefer</feature> <!-- Prefer header overall support -->
- <feature>prefer-minimal</feature> <!-- Prefer header return=minimal -->
- <feature>prefer-representation</feature> <!-- Prefer header return=representation -->
- <feature>prefer-noroot</feature> <!-- Prefer header depth-noroot -->
- <feature>quota</feature> <!-- WebDAV QUOTA -->
- <!-- <feature>quota-on-resources</feature> --> <!-- WebDAV QUOTA on calendar and address book object resources -->
- <feature>resource-id</feature> <!-- WebDAV BIND DAV:resource-id property -->
- <feature>sync-report</feature> <!-- WebDAV collection sync REPORT -->
- <!-- <feature>sync-report-limit</feature> --> <!-- WebDAV collection sync REPORT DAV:limit support -->
- <feature>sync-report-home</feature> <!-- WebDAV collection sync REPORT on Homes -->
- <feature>well-known</feature> <!-- well-known feature -->
-
- <!-- <feature>per-object-ACLs</feature> --> <!-- ACL for objects in calendar/address books -->
- <!-- <feature>regular-collection</feature> --> <!-- Regular collections allowed in calendar/address book homes -->
-
- <!-- <feature>json-data</feature> --> <!-- jCal and jCard support -->
-
- <!-- CalDAV specific extension -->
- <feature>caldav</feature> <!-- Basic CalDAV feature enabler -->
- <feature>attachments-collection</feature> <!-- Server uses a collection in same WebDAV tree to store attachments -->
- <feature>auto-accept</feature> <!-- Auto-accept for rooms & locations -->
- <feature>auto-accept-modes</feature> <!-- Auto-accept modes -->
- <!-- <feature>dropbox</feature> --> <!-- dropbox extension -->
- <feature>default-alarms</feature> <!-- default alarms extension -->
- <feature>EMAIL parameter</feature> <!-- Server normalizes cuaddress and adds EMAIL parameter -->
- <feature>extended-freebusy</feature> <!-- Extended freebusy response -->
- <feature>implicit-scheduling</feature> <!-- CalDAV scheduling - implicit -->
- <feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
- <feature>managed-attachments</feature> <!-- CalDAV Managed Attachments -->
- <feature>maskuid</feature> <!-- maskuid extension -->
- <feature>no-duplicate-uids</feature> <!-- duplicate UIDs in same home not supported -->
- <feature>partitioning</feature> <!-- Partitioned server -->
- <feature>partstat-timestamp</feature> <!-- Time stamps when PARTSTAT changes extension -->
- <feature>private-comments</feature> <!-- private-comments extension -->
- <feature>private-events</feature> <!-- private-events extension -->
- <feature>proxy</feature> <!-- calendar-user-proxy extension -->
- <!-- <feature>proxy-authz</feature> --> <!-- sudo user extension -->
- <feature>remove-duplicate-alarms</feature> <!-- Server removes any duplicate alarms on PUT -->
- <feature>query-extended</feature> <!-- calendar-query-extended extension -->
- <feature>shared-calendars</feature> <!-- Shared calendars extension -->
- <feature>schedule-changes</feature> <!-- schedule-changes property extension -->
- <feature>split-calendars</feature> <!-- Calendars are split by component type -->
- <feature>supported-component-sets</feature> <!-- CALDAV:supported-calendar-component-sets on calendar homes -->
- <feature>supported-component-sets-one</feature> <!-- Only single component calendars allowed to be created -->
- <feature>timerange-low-limit</feature> <!-- Time-range only valid one year back -->
- <feature>timerange-high-limit</feature> <!-- Time-range only valid 5 years ahead -->
- <feature>timezones-by-reference</feature> <!-- Timezones by reference enabled -->
- <feature>timezone-service</feature> <!-- Timezone service extension for Wiki -->
- <feature>timezone-std-service</feature> <!-- Timezone standard service extension -->
- <feature>vavailability</feature> <!-- VAVAILABILITY on inbox -->
- <feature>webcal</feature> <!-- Internet calendar subscription via GET on calendar collection -->
-
- <!-- CardDAV specific extension -->
- <feature>carddav</feature> <!-- Basic CardDAV feature enabler -->
- <feature>default-addressbook</feature> <!-- Default address book behavior -->
- <!-- <feature>global-addressbook</feature> --> <!-- Global address book for each user -->
- <feature>shared-addressbooks</feature> <!-- Shared address books extension -->
- <!-- <feature>directory-gateway</feature> --> <!-- Directory gateway extension -->
-
- </features>
-
- <substitutions>
- <!-- Useful xpath shortcuts for verifiers -->
- <substitution>
- <key>$multistatus-response-prefix:</key>
- <value>/{DAV:}multistatus/{DAV:}response</value>
- </substitution>
- <substitution>
- <key>$multistatus-href-prefix:</key>
- <value>/{DAV:}multistatus/{DAV:}response/{DAV:}href</value>
- </substitution>
- <substitution>
- <key>$verify-response-prefix:</key>
- <value>{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
- </substitution>
- <substitution>
- <key>$verify-property-prefix:</key>
- <value>/{DAV:}multistatus/{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
- </substitution>
- <substitution>
- <key>$verify-bad-response:</key>
- <value>/{DAV:}multistatus/{DAV:}response/{DAV:}status</value>
- </substitution>
- <substitution>
- <key>$verify-error-response:</key>
- <value>/{DAV:}multistatus/{DAV:}response/{DAV:}error</value>
- </substitution>
- <substitution>
- <key>$CALDAV:</key>
- <value>urn:ietf:params:xml:ns:caldav</value>
- </substitution>
- <substitution>
- <key>$CARDDAV:</key>
- <value>urn:ietf:params:xml:ns:carddav</value>
- </substitution>
- <substitution>
- <key>$CS:</key>
- <value>http://calendarserver.org/ns/</value>
- </substitution>
-
- <!-- Server configuration settings -->
- <!-- $host: and $hostssl: are implicitly added by CalDAVTester based
- on the host/nonsslport/sslport values and ssl command line switch -->
-
- <!-- relative path to caldav root-->
- <substitution>
- <key>$root:</key>
- <value>/</value>
- </substitution>
-
- <!-- relative path to main principal collection-->
- <substitution>
- <key>$principalcollection:</key>
- <value>$root:principals/</value>
- </substitution>
-
- <!-- the core recored type collections-->
- <substitution>
- <key>$uidstype:</key>
- <value>__uids__</value>
- </substitution>
- <substitution>
- <key>$userstype:</key>
- <value>users</value>
- </substitution>
- <substitution>
- <key>$groupstype:</key>
- <value>groups</value>
- </substitution>
- <substitution>
- <key>$locationstype:</key>
- <value>locations</value>
- </substitution>
- <substitution>
- <key>$resourcestype:</key>
- <value>resources</value>
- </substitution>
-
- <!-- relative path to record type principal collections-->
- <substitution>
- <key>$principals_uids:</key>
- <value>$principalcollection:$uidstype:/</value>
- </substitution>
- <substitution>
- <key>$principals_users:</key>
- <value>$principalcollection:$userstype:/</value>
- </substitution>
- <substitution>
- <key>$principals_groups:</key>
- <value>$principalcollection:$groupstype:/</value>
- </substitution>
- <substitution>
- <key>$principals_resources:</key>
- <value>$principalcollection:$resourcestype:/</value>
- </substitution>
- <substitution>
- <key>$principals_locations:</key>
- <value>$principalcollection:$locationstype:/</value>
- </substitution>
-
- <!-- relative path to calendars collection-->
- <substitution>
- <key>$calendars:</key>
- <value>$root:calendars/</value>
- </substitution>
-
- <!-- relative path to record type calendar collections-->
- <substitution>
- <key>$calendars_uids:</key>
- <value>$calendars:$uidstype:/</value>
- </substitution>
- <substitution>
- <key>$calendars_users:</key>
- <value>$calendars:$userstype:/</value>
- </substitution>
- <substitution>
- <key>$calendars_groups:</key>
- <value>$calendars:$groupstype:/</value>
- </substitution>
- <substitution>
- <key>$calendars_resources:</key>
- <value>$calendars:$resourcestype:/</value>
- </substitution>
- <substitution>
- <key>$calendars_locations:</key>
- <value>$calendars:$locationstype:/</value>
- </substitution>
-
- <!-- primary calendar name-->
- <substitution>
- <key>$calendar:</key>
- <value>calendar</value>
- </substitution>
-
- <!-- primary tasks-only calendar name-->
- <substitution>
- <key>$tasks:</key>
- <value>tasks</value>
- </substitution>
-
- <!-- inbox name-->
- <substitution>
- <key>$inbox:</key>
- <value>inbox</value>
- </substitution>
-
- <!-- outbox name-->
- <substitution>
- <key>$outbox:</key>
- <value>outbox</value>
- </substitution>
-
- <!-- dropbox name-->
- <substitution>
- <key>$dropbox:</key>
- <value>dropbox</value>
- </substitution>
-
- <!-- attachments name-->
- <substitution>
- <key>$attachments:</key>
- <value>dropbox</value>
- </substitution>
-
- <!-- notification name-->
- <substitution>
- <key>$notification:</key>
- <value>notification</value>
- </substitution>
-
- <!-- freebusy name-->
- <substitution>
- <key>$freebusy:</key>
- <value>freebusy</value>
- </substitution>
-
- <!-- server-to-server inbox-->
- <substitution>
- <key>$servertoserver:</key>
- <value>$root:inbox</value>
- </substitution>
-
- <!-- timezone service-->
- <substitution>
- <key>$timezoneservice:</key>
- <value>$root:timezones</value>
- </substitution>
-
- <!-- timezone std service-->
- <substitution>
- <key>$timezonestdservice:</key>
- <value>$root:stdtimezones</value>
- </substitution>
-
- <!-- relative path to addressbooks collection-->
- <substitution>
- <key>$addressbooks:</key>
- <value>$root:addressbooks/</value>
- </substitution>
-
- <!-- relative path to record type addressbook collections-->
- <substitution>
- <key>$addressbooks_uids:</key>
- <value>$addressbooks:$uidstype:/</value>
- </substitution>
- <substitution>
- <key>$addressbooks_users:</key>
- <value>$addressbooks:$userstype:/</value>
- </substitution>
- <substitution>
- <key>$addressbooks_groups:</key>
- <value>$addressbooks:$groupstype:/</value>
- </substitution>
-
- <!-- primary addressbook name -->
- <substitution>
- <key>$addressbook:</key>
- <value>addressbook</value>
- </substitution>
-
- <!-- directory name -->
- <substitution>
- <key>$directory:</key>
- <value>$root:directory/</value>
- </substitution>
-
- <!-- global-addressbook name -->
- <substitution>
- <key>$global_addressbook:</key>
- <value>global-addressbook</value>
- </substitution>
-
- <!-- POST add-member URI suffix -->
- <substitution>
- <key>$add-member:</key>
- <value>;add-member</value>
- </substitution>
-
- <!-- user id for admin user -->
- <substitution>
- <key>$useradmin:</key>
- <value>admin</value>
- </substitution>
- <!-- guid for admin user -->
- <substitution>
- <key>$useradminguid:</key>
- <value>admin</value>
- </substitution>
- <!-- password for admin user -->
- <substitution>
- <key>$pswdadmin:</key>
- <value>admin</value>
- </substitution>
-
- <!-- relative path to admin principal resource-->
- <substitution>
- <key>$principal_admin:</key>
- <value>$principals_users:$useradmin:/</value>
- </substitution>
- <substitution>
- <key>$principaluri_admin:</key>
- <value>$principals_uids:$useradminguid:/</value>
- </substitution>
-
- <!-- user id for apprentice user -->
- <substitution>
- <key>$userapprentice:</key>
- <value>apprentice</value>
- </substitution>
- <!-- guid for apprentice user -->
- <substitution>
- <key>$userapprenticeguid:</key>
- <value>apprentice</value>
- </substitution>
- <!-- password for admin user -->
- <substitution>
- <key>$pswdapprentice:</key>
- <value>apprentice</value>
- </substitution>
-
- <!-- relative path to apprentice principal resource-->
- <substitution>
- <key>$principal_apprentice:</key>
- <value>$principals_users:$userapprentice:/</value>
- </substitution>
- <substitution>
- <key>$principaluri_apprentice:</key>
- <value>$principals_uids:$userapprenticeguid:/</value>
- </substitution>
-
- <!-- user id for proxy user -->
- <substitution>
- <key>$userproxy:</key>
- <value>superuser</value>
- </substitution>
- <!-- password for proxy user -->
- <substitution>
- <key>$pswdproxy:</key>
- <value>superuser</value>
- </substitution>
-
- <!-- Forty user accounts -->
- <repeat count="40">
- <!-- user id -->
- <substitution>
- <key>$userid%d:</key>
- <value>user%02d</value>
- </substitution>
- <!-- user guid -->
- <substitution>
- <key>$userguid%d:</key>
- <value>user%02d</value>
- </substitution>
- <!-- user name -->
- <substitution>
- <key>$username%d:</key>
- <value>User %02d</value>
- </substitution>
- <!-- user name URI encoded -->
- <substitution>
- <key>$username-encoded%d:</key>
- <value>User%%20%02d</value>
- </substitution>
- <!-- first name -->
- <substitution>
- <key>$firstname%d:</key>
- <value>User</value>
- </substitution>
- <!-- last name -->
- <substitution>
- <key>$lastname%d:</key>
- <value>%02d</value>
- </substitution>
- <!-- password -->
- <substitution>
- <key>$pswd%d:</key>
- <value>user%02d</value>
- </substitution>
- <!-- relative path to user principal resource-->
- <substitution>
- <key>$principal%d:</key>
- <value>$principals_users:$userid%d:/</value>
- </substitution>
- <substitution>
- <key>$principaluri%d:</key>
- <value>$principals_uids:$userguid%d:/</value>
- </substitution>
- <substitution>
- <key>$principal%dnoslash:</key>
- <value>$principals_users:$userid%d:</value>
- </substitution>
-
- <!-- relative path to user calendar home-->
- <substitution>
- <key>$calendarhome%d:</key>
- <value>$calendars_uids:$userguid%d:</value>
- </substitution>
- <!-- relative path to user alternate calendar home-->
- <substitution>
- <key>$calendarhomealt%d:</key>
- <value>$calendars_users:$userid%d:</value>
- </substitution>
- <!-- relative path to user calendar-->
- <substitution>
- <key>$calendarpath%d:</key>
- <value>$calendarhome%d:/$calendar:</value>
- </substitution>
- <!-- relative path to user alternate calendar-->
- <substitution>
- <key>$calendarpathalt%d:</key>
- <value>$calendarhomealt%d:/$calendar:</value>
- </substitution>
- <!-- relative path to user tasks calendar-->
- <substitution>
- <key>$taskspath%d:</key>
- <value>$calendarhome%d:/$tasks:</value>
- </substitution>
- <!-- relative path to user inbox-->
- <substitution>
- <key>$inboxpath%d:</key>
- <value>$calendarhome%d:/$inbox:</value>
- </substitution>
- <!-- relative path to user outbox-->
- <substitution>
- <key>$outboxpath%d:</key>
- <value>$calendarhome%d:/$outbox:</value>
- </substitution>
- <!-- relative path to user dropbox-->
- <substitution>
- <key>$dropboxpath%d:</key>
- <value>$calendarhome%d:/$dropbox:</value>
- </substitution>
- <!-- relative path to user notification-->
- <substitution>
- <key>$notificationpath%d:</key>
- <value>$calendarhome%d:/$notification:</value>
- </substitution>
- <!-- relative path to user freebusy-->
- <substitution>
- <key>$freebusypath%d:</key>
- <value>$calendarhome%d:/$freebusy:</value>
- </substitution>
- <substitution>
- <key>$email%d:</key>
- <value>$userid%d:@example.com</value>
- </substitution>
- <!-- calendar user address of user-->
- <substitution>
- <key>$cuaddr%d:</key>
- <value>mailto:$email%d:</value>
- </substitution>
- <substitution>
- <key>$cuaddralt%d:</key>
- <value>$principaluri%d:</value>
- </substitution>
- <substitution>
- <key>$cuaddraltnoslash%d:</key>
- <value>$principals_uids:$userguid%d:</value>
- </substitution>
- <substitution>
- <key>$cuaddrurn%d:</key>
- <value>urn:uuid:$userguid%d:</value>
- </substitution>
-
- <!-- relative path to user addressbook home-->
- <substitution>
- <key>$addressbookhome%d:</key>
- <value>$addressbooks_uids:$userguid%d:</value>
- </substitution>
- <!-- relative path to user addressbook-->
- <substitution>
- <key>$addressbookpath%d:</key>
- <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
- </substitution>
- </repeat>
-
- <!-- Ten public accounts -->
- <repeat count="10">
- <!-- user id -->
- <substitution>
- <key>$publicuserid%d:</key>
- <value>public%02d</value>
- </substitution>
- <!-- user guid -->
- <substitution>
- <key>$publicuserguid%d:</key>
- <value>public%02d</value>
- </substitution>
- <!-- user name -->
- <substitution>
- <key>$publicusername%d:</key>
- <value>Public %02d</value>
- </substitution>
- <!-- password -->
- <substitution>
- <key>$publicpswd%d:</key>
- <value>public%02d</value>
- </substitution>
- <!-- relative path to user principal resource-->
- <substitution>
- <key>$publicprincipal%d:</key>
- <value>$principals_users:$publicuserid%d:/</value>
- </substitution>
- <substitution>
- <key>$publicprincipaluri%d:</key>
- <value>$principals_uids:$publicuserguid%d:/</value>
- </substitution>
- <!-- relative path to user calendar home-->
- <substitution>
- <key>$publiccalendarhome%d:</key>
- <value>$calendars_uids:$publicuserguid%d:</value>
- </substitution>
- <!-- relative path to user calendar-->
- <substitution>
- <key>$publiccalendarpath%d:</key>
- <value>$calendars_uids:$publicuserguid%d:/$calendar:</value>
- </substitution>
- <substitution>
- <key>$publicemail%d:</key>
- <value>$publicuserid%d:@example.com</value>
- </substitution>
- <!-- calendar user address of user-->
- <substitution>
- <key>$publiccuaddr%d:</key>
- <value>mailto:$publicemail%d:</value>
- </substitution>
- <substitution>
- <key>$publiccuaddralt%d:</key>
- <value>$publicprincipaluri%d:</value>
- </substitution>
- <substitution>
- <key>$publiccuaddrurn%d:</key>
- <value>urn:uuid:$publicuserguid%d:</value>
- </substitution>
- </repeat>
-
- <!-- Twenty resource accounts -->
- <repeat count="20">
- <substitution>
- <key>$resourceid%d:</key>
- <value>resource%02d</value>
- </substitution>
- <!-- resource guid-->
- <substitution>
- <key>$resourceguid%d:</key>
- <value>resource%02d</value>
- </substitution>
- <!-- resource name-->
- <substitution>
- <key>$resourcename%d:</key>
- <value>Resource %02d</value>
- </substitution>
- <!-- relative path to first resource calendar home-->
- <substitution>
- <key>$rcalendarhome%d:</key>
- <value>$calendars_uids:$resourceguid%d:</value>
- </substitution>
- <!-- relative path to first resource calendar home-->
- <substitution>
- <key>$rcalendarpath%d:</key>
- <value>$calendars_uids:$resourceguid%d:/$calendar:</value>
- </substitution>
- <!-- relative path to first resource inbox-->
- <substitution>
- <key>$rinboxpath%d:</key>
- <value>$calendars_uids:$resourceguid%d:/$inbox:</value>
- </substitution>
- <!-- relative path to first resource outbox-->
- <substitution>
- <key>$routboxpath%d:</key>
- <value>$calendars_uids:$resourceguid%d:/$outbox:</value>
- </substitution>
- <!-- relative path to first resource principal resource-->
- <substitution>
- <key>$rprincipal%d:</key>
- <value>$principals_resources:$resourceid%d:/</value>
- </substitution>
- <substitution>
- <key>$rprincipaluri%d:</key>
- <value>$principals_uids:$resourceguid%d:/</value>
- </substitution>
- <substitution>
- <key>$rcuaddralt%d:</key>
- <value>$rprincipaluri%d:</value>
- </substitution>
- <substitution>
- <key>$rcuaddrurn%d:</key>
- <value>urn:uuid:$resourceguid%d:</value>
- </substitution>
- </repeat>
-
- <!-- Ten Location accounts -->
- <repeat count="10">
- <substitution>
- <key>$locationid%d:</key>
- <value>location%02d</value>
- </substitution>
- <!-- location guid-->
- <substitution>
- <key>$locationguid%d:</key>
- <value>location%02d</value>
- </substitution>
- <!-- location name-->
- <substitution>
- <key>$locationname%d:</key>
- <value>Location %02d</value>
- </substitution>
- <!-- relative path to first location calendar home-->
- <substitution>
- <key>$lcalendarhome%d:</key>
- <value>$calendars_uids:$locationguid%d:</value>
- </substitution>
- <!-- relative path to first location calendar home-->
- <substitution>
- <key>$lcalendarpath%d:</key>
- <value>$calendars_uids:$locationguid%d:/$calendar:</value>
- </substitution>
- <!-- relative path to first location inbox-->
- <substitution>
- <key>$linboxpath%d:</key>
- <value>$calendars_uids:$locationguid%d:/$inbox:</value>
- </substitution>
- <!-- relative path to first location outbox-->
- <substitution>
- <key>$loutboxpath%d:</key>
- <value>$calendars_uids:$locationguid%d:/$outbox:</value>
- </substitution>
- <!-- relative path to first location principal resource-->
- <substitution>
- <key>$lprincipal%d:</key>
- <value>$principals_resources:$locationid%d:/</value>
- </substitution>
- <substitution>
- <key>$lprincipaluri%d:</key>
- <value>$principals_uids:$locationguid%d:/</value>
- </substitution>
- <substitution>
- <key>$lcuaddralt%d:</key>
- <value>$lprincipaluri%d:</value>
- </substitution>
- <substitution>
- <key>$lcuaddrurn%d:</key>
- <value>urn:uuid:$locationguid%d:</value>
- </substitution>
- </repeat>
-
-
- <!-- Ten Group accounts -->
- <repeat count="10">
- <substitution>
- <key>$groupid%d:</key>
- <value>group%02d</value>
- </substitution>
- <!-- group guid-->
- <substitution>
- <key>$groupguid%d:</key>
- <value>group%02d</value>
- </substitution>
- <!-- group name-->
- <substitution>
- <key>$groupname%d:</key>
- <value>Group %02d</value>
- </substitution>
- <!-- relative path to first group principal resource-->
- <substitution>
- <key>$gprincipal%d:</key>
- <value>$principals_resources:$groupid%d:/</value>
- </substitution>
- <substitution>
- <key>$gprincipaluri%d:</key>
- <value>$principals_uids:$groupguid%d:/</value>
- </substitution>
- <substitution>
- <key>$gcuaddralt%d:</key>
- <value>$gprincipaluri%d:</value>
- </substitution>
- <substitution>
- <key>$gcuaddrurn%d:</key>
- <value>urn:uuid:$groupguid%d:</value>
- </substitution>
- </repeat>
-
- <!-- User with non-ascii name -->
- <substitution>
- <key>$i18nid:</key>
- <value>i18nuser</value>
- </substitution>
- <!-- group guid-->
- <substitution>
- <key>$i18nguid:</key>
- <value>i18nuser</value>
- </substitution>
- <!-- group name-->
- <substitution>
- <key>$i18nname:</key>
- <value>まだ</value>
- </substitution>
- <!-- password -->
- <substitution>
- <key>$i18npswd:</key>
- <value>i18nuser</value>
- </substitution>
- <!-- relative path to user calendar-->
- <substitution>
- <key>$i18ncalendarpath:</key>
- <value>$calendars_uids:$i18nguid:/$calendar:</value>
- </substitution>
- <substitution>
- <key>$i18nemail:</key>
- <value>$i18nid:@example.com</value>
- </substitution>
- <!-- CUAddrs -->
- <substitution>
- <key>$i18ncuaddr:</key>
- <value>mailto:$i18nemail:</value>
- </substitution>
- <substitution>
- <key>$i18ncuaddrurn:</key>
- <value>urn:uuid:$i18nguid:</value>
- </substitution>
-
- <!-- relative path to disabled group principal resource-->
- <substitution>
- <key>$principaldisabled:</key>
- <value>$principals_groups:disabledgroup/</value>
- </substitution>
- <substitution>
- <key>$principaluridisabled:</key>
- <value>$principals_uids:disabledgroup/</value>
- </substitution>
- <!-- calendar user address of disabled group-->
- <substitution>
- <key>$cuaddrdisabled:</key>
- <value>$principals_uids:disabledgroup/</value>
- </substitution>
-
- <!-- Ten other accounts -->
- <repeat count="10">
- <!-- user id -->
- <substitution>
- <key>$otherid%d:</key>
- <value>other%02d</value>
- </substitution>
- <!-- user guid -->
- <substitution>
- <key>$otherguid%d:</key>
- <value>other%02d</value>
- </substitution>
- <!-- user name -->
- <substitution>
- <key>$otherusername%d:</key>
- <value>Other %02d</value>
- </substitution>
- <!-- password -->
- <substitution>
- <key>$otherpswd%d:</key>
- <value>other%02d</value>
- </substitution>
- <!-- relative path to user principal resource-->
- <substitution>
- <key>$otherprincipal%d:</key>
- <value>$principals_users:$otherid%d:/</value>
- </substitution>
- <substitution>
- <key>$otherprincipaluri%d:</key>
- <value>$principals_uids:$otherguid%d:/</value>
- </substitution>
- <!-- relative path to user calendar home-->
- <substitution>
- <key>$othercalendarhome%d:</key>
- <value>$calendars_uids:$otherguid%d:</value>
- </substitution>
- <!-- relative path to user calendar-->
- <substitution>
- <key>$othercalendarpath%d:</key>
- <value>$calendars_uids:$otherguid%d:/$calendar:</value>
- </substitution>
- <!-- relative path to user inbox-->
- <substitution>
- <key>$otherinboxpath%d:</key>
- <value>$calendars_uids:$otherguid%d:/$inbox:</value>
- </substitution>
- <!-- relative path to user outbox-->
- <substitution>
- <key>$otheroutboxpath%d:</key>
- <value>$calendars_uids:$otherguid%d:/$outbox:</value>
- </substitution>
- <!-- relative path to user dropbox-->
- <substitution>
- <key>$otherdropboxpath%d:</key>
- <value>$calendars_uids:$otherguid%d:/$dropbox:</value>
- </substitution>
- <!-- relative path to user freebusy-->
- <substitution>
- <key>$otherfreebusypath%d:</key>
- <value>$calendars_uids:$otherguid%d:/$freebusy:</value>
- </substitution>
- <!-- calendar user address of user-->
- <substitution>
- <key>$otheremail%d:</key>
- <value>$otherid%d:@example.com</value>
- </substitution>
- <substitution>
- <key>$othercuaddr%d:</key>
- <value>mailto:$otheremail%d:</value>
- </substitution>
- <substitution>
- <key>$othercuaddralt%d:</key>
- <value>$otherprincipaluri%d:</value>
- </substitution>
- <substitution>
- <key>$othercuaddrurn%d:</key>
- <value>urn:uuid:$otherguid%d:</value>
- </substitution>
- </repeat>
-
- <!-- Override some of the above definitions for special cases -->
-
- <!-- calendar user address of second user-->
- <substitution>
- <key>$cuaddr2:</key>
- <value>MAILTO:$email2:</value>
- </substitution>
-
- </substitutions>
-</serverinfo>
Copied: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml (from rev 12016, CalDAVTester/trunk/scripts/server/serverinfo-pod.xml)
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml (rev 0)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,652 @@
+<?xml version="1.0" standalone="no"?>
+
+<!DOCTYPE serverinfo SYSTEM "serverinfo.dtd">
+
+<!--
+ Copyright (c) 2006-2013 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.
+ -->
+
+<serverinfo>
+ <host>localhost</host>
+ <nonsslport>8008</nonsslport>
+ <sslport>8443</sslport>
+
+ <host2>localhost</host2>
+ <nonsslport2>8108</nonsslport2>
+ <sslport2>8543</sslport2>
+
+ <authtype>basic</authtype>
+
+ <features>
+ <!-- Generic WebDAV extensions -->
+ <feature>COPY Method</feature> <!-- COPY method -->
+ <feature>MOVE Method</feature> <!-- MOVE method -->
+ <feature>Extended MKCOL</feature> <!-- Extended MKCOL -->
+
+ <!-- ACL related -->
+ <feature>ACL Method</feature> <!-- ACL method -->
+ <feature>acl-principal-prop-set REPORT</feature> <!-- ACL acl-principal-prop-set REPORT -->
+ <feature>principal-match REPORT</feature> <!-- ACL principal-match REPORT -->
+ <feature>principal-property-search REPORT</feature> <!-- ACL principal-property-search REPORT -->
+ <feature>principal-search-property-set REPORT</feature> <!-- ACL principal-search-property-set REPORT -->
+
+ <feature>add-member</feature> <!-- Add-member used to create resources -->
+ <!-- <feature>auth-on-root</feature> --> <!-- Whether the server requires authentication on the root URI -->
+ <feature>brief</feature> <!-- Brief header for PROPFIND, REPORT -->
+ <feature>bulk-post</feature> <!-- Bulk POST requests -->
+ <feature>ctag</feature> <!-- ctag extension -->
+ <feature>current-user-principal</feature> <!-- current-user-principal extension -->
+ <feature>directory listing</feature> <!-- GET on collection -->
+ <feature>extended-principal-search</feature> <!-- Extended principal-property-search REPORT extension -->
+ <feature>expand-property</feature> <!-- Expand property REPORT -->
+ <feature>only-proxy-groups</feature> <!-- Group-membership only includes delegated-to groups -->
+ <feature>limits</feature> <!-- max-collections and max-resources limits -->
+ <feature>prefer</feature> <!-- Prefer header overall support -->
+ <feature>prefer-minimal</feature> <!-- Prefer header return=minimal -->
+ <feature>prefer-representation</feature> <!-- Prefer header return=representation -->
+ <feature>prefer-noroot</feature> <!-- Prefer header depth-noroot -->
+ <feature>quota</feature> <!-- WebDAV QUOTA -->
+ <!-- <feature>quota-on-resources</feature> --> <!-- WebDAV QUOTA on calendar and address book object resources -->
+ <feature>resource-id</feature> <!-- WebDAV BIND DAV:resource-id property -->
+ <feature>sync-report</feature> <!-- WebDAV collection sync REPORT -->
+ <!-- <feature>sync-report-limit</feature> --> <!-- WebDAV collection sync REPORT DAV:limit support -->
+ <feature>sync-report-home</feature> <!-- WebDAV collection sync REPORT on Homes -->
+ <feature>well-known</feature> <!-- well-known feature -->
+
+ <!-- <feature>per-object-ACLs</feature> --> <!-- ACL for objects in calendar/address books -->
+ <!-- <feature>regular-collection</feature> --> <!-- Regular collections allowed in calendar/address book homes -->
+
+ <!-- <feature>json-data</feature> --> <!-- jCal and jCard support -->
+
+ <!-- CalDAV specific extension -->
+ <feature>caldav</feature> <!-- Basic CalDAV feature enabler -->
+ <feature>attachments-collection</feature> <!-- Server uses a collection in same WebDAV tree to store attachments -->
+ <feature>auto-accept</feature> <!-- Auto-accept for rooms & locations -->
+ <feature>auto-accept-modes</feature> <!-- Auto-accept modes -->
+ <!-- <feature>dropbox</feature> --> <!-- dropbox extension -->
+ <feature>default-alarms</feature> <!-- default alarms extension -->
+ <feature>EMAIL parameter</feature> <!-- Server normalizes cuaddress and adds EMAIL parameter -->
+ <feature>extended-freebusy</feature> <!-- Extended freebusy response -->
+ <feature>implicit-scheduling</feature> <!-- CalDAV scheduling - implicit -->
+ <feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
+ <feature>managed-attachments</feature> <!-- CalDAV Managed Attachments -->
+ <feature>maskuid</feature> <!-- maskuid extension -->
+ <feature>no-duplicate-uids</feature> <!-- duplicate UIDs in same home not supported -->
+ <feature>partstat-timestamp</feature> <!-- Time stamps when PARTSTAT changes extension -->
+ <feature>podding</feature> <!-- Podded server -->
+ <feature>private-comments</feature> <!-- private-comments extension -->
+ <feature>private-events</feature> <!-- private-events extension -->
+ <feature>proxy</feature> <!-- calendar-user-proxy extension -->
+ <!-- <feature>proxy-authz</feature> --> <!-- sudo user extension -->
+ <feature>remove-duplicate-alarms</feature> <!-- Server removes any duplicate alarms on PUT -->
+ <feature>query-extended</feature> <!-- calendar-query-extended extension -->
+ <feature>shared-calendars</feature> <!-- Shared calendars extension -->
+ <feature>schedule-changes</feature> <!-- schedule-changes property extension -->
+ <feature>split-calendars</feature> <!-- Calendars are split by component type -->
+ <feature>supported-component-sets</feature> <!-- CALDAV:supported-calendar-component-sets on calendar homes -->
+ <feature>supported-component-sets-one</feature> <!-- Only single component calendars allowed to be created -->
+ <feature>timerange-low-limit</feature> <!-- Time-range only valid one year back -->
+ <feature>timerange-high-limit</feature> <!-- Time-range only valid 5 years ahead -->
+ <feature>timezones-by-reference</feature> <!-- Timezones by reference enabled -->
+ <feature>timezone-service</feature> <!-- Timezone service extension for Wiki -->
+ <feature>timezone-std-service</feature> <!-- Timezone standard service extension -->
+ <feature>vavailability</feature> <!-- VAVAILABILITY on inbox -->
+ <!-- <feature>vpoll</feature> --> <!-- VPOLL support for store and scheduling -->
+ <feature>webcal</feature> <!-- Internet calendar subscription via GET on calendar collection -->
+
+ <!-- CardDAV specific extension -->
+ <feature>carddav</feature> <!-- Basic CardDAV feature enabler -->
+ <feature>default-addressbook</feature> <!-- Default address book behavior -->
+ <!-- <feature>global-addressbook</feature> --> <!-- Global address book for each user -->
+ <feature>shared-addressbooks</feature> <!-- Shared address books extension -->
+ <!-- <feature>directory-gateway</feature> --> <!-- Directory gateway extension -->
+
+ </features>
+
+ <substitutions>
+ <!-- Useful xpath shortcuts for verifiers -->
+ <substitution>
+ <key>$multistatus-response-prefix:</key>
+ <value>/{DAV:}multistatus/{DAV:}response</value>
+ </substitution>
+ <substitution>
+ <key>$multistatus-href-prefix:</key>
+ <value>/{DAV:}multistatus/{DAV:}response/{DAV:}href</value>
+ </substitution>
+ <substitution>
+ <key>$verify-response-prefix:</key>
+ <value>{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
+ </substitution>
+ <substitution>
+ <key>$verify-property-prefix:</key>
+ <value>/{DAV:}multistatus/{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
+ </substitution>
+ <substitution>
+ <key>$verify-bad-response:</key>
+ <value>/{DAV:}multistatus/{DAV:}response/{DAV:}status</value>
+ </substitution>
+ <substitution>
+ <key>$verify-error-response:</key>
+ <value>/{DAV:}multistatus/{DAV:}response/{DAV:}error</value>
+ </substitution>
+ <substitution>
+ <key>$CALDAV:</key>
+ <value>urn:ietf:params:xml:ns:caldav</value>
+ </substitution>
+ <substitution>
+ <key>$CARDDAV:</key>
+ <value>urn:ietf:params:xml:ns:carddav</value>
+ </substitution>
+ <substitution>
+ <key>$CS:</key>
+ <value>http://calendarserver.org/ns/</value>
+ </substitution>
+
+ <!-- Server configuration settings -->
+ <!-- $host: and $hostssl: are implicitly added by CalDAVTester based
+ on the host/nonsslport/sslport values and ssl command line switch -->
+
+ <!-- relative path to caldav root-->
+ <substitution>
+ <key>$root:</key>
+ <value>/</value>
+ </substitution>
+
+ <!-- relative path to main principal collection-->
+ <substitution>
+ <key>$principalcollection:</key>
+ <value>$root:principals/</value>
+ </substitution>
+
+ <!-- the core recored type collections-->
+ <substitution>
+ <key>$uidstype:</key>
+ <value>__uids__</value>
+ </substitution>
+ <substitution>
+ <key>$userstype:</key>
+ <value>users</value>
+ </substitution>
+ <substitution>
+ <key>$groupstype:</key>
+ <value>groups</value>
+ </substitution>
+ <substitution>
+ <key>$locationstype:</key>
+ <value>locations</value>
+ </substitution>
+ <substitution>
+ <key>$resourcestype:</key>
+ <value>resources</value>
+ </substitution>
+
+ <!-- relative path to record type principal collections-->
+ <substitution>
+ <key>$principals_uids:</key>
+ <value>$principalcollection:$uidstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$principals_users:</key>
+ <value>$principalcollection:$userstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$principals_groups:</key>
+ <value>$principalcollection:$groupstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$principals_resources:</key>
+ <value>$principalcollection:$resourcestype:/</value>
+ </substitution>
+ <substitution>
+ <key>$principals_locations:</key>
+ <value>$principalcollection:$locationstype:/</value>
+ </substitution>
+
+ <!-- relative path to calendars collection-->
+ <substitution>
+ <key>$calendars:</key>
+ <value>$root:calendars/</value>
+ </substitution>
+
+ <!-- relative path to record type calendar collections-->
+ <substitution>
+ <key>$calendars_uids:</key>
+ <value>$calendars:$uidstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$calendars_users:</key>
+ <value>$calendars:$userstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$calendars_groups:</key>
+ <value>$calendars:$groupstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$calendars_resources:</key>
+ <value>$calendars:$resourcestype:/</value>
+ </substitution>
+ <substitution>
+ <key>$calendars_locations:</key>
+ <value>$calendars:$locationstype:/</value>
+ </substitution>
+
+ <!-- primary calendar name-->
+ <substitution>
+ <key>$calendar:</key>
+ <value>calendar</value>
+ </substitution>
+
+ <!-- primary tasks-only calendar name-->
+ <substitution>
+ <key>$tasks:</key>
+ <value>tasks</value>
+ </substitution>
+
+ <!-- inbox name-->
+ <substitution>
+ <key>$inbox:</key>
+ <value>inbox</value>
+ </substitution>
+
+ <!-- outbox name-->
+ <substitution>
+ <key>$outbox:</key>
+ <value>outbox</value>
+ </substitution>
+
+ <!-- dropbox name-->
+ <substitution>
+ <key>$dropbox:</key>
+ <value>dropbox</value>
+ </substitution>
+
+ <!-- attachments name-->
+ <substitution>
+ <key>$attachments:</key>
+ <value>dropbox</value>
+ </substitution>
+
+ <!-- notification name-->
+ <substitution>
+ <key>$notification:</key>
+ <value>notification</value>
+ </substitution>
+
+ <!-- freebusy name-->
+ <substitution>
+ <key>$freebusy:</key>
+ <value>freebusy</value>
+ </substitution>
+
+ <!-- server-to-server inbox-->
+ <substitution>
+ <key>$servertoserver:</key>
+ <value>$root:inbox</value>
+ </substitution>
+
+ <!-- timezone service-->
+ <substitution>
+ <key>$timezoneservice:</key>
+ <value>$root:timezones</value>
+ </substitution>
+
+ <!-- timezone std service-->
+ <substitution>
+ <key>$timezonestdservice:</key>
+ <value>$root:stdtimezones</value>
+ </substitution>
+
+ <!-- relative path to addressbooks collection-->
+ <substitution>
+ <key>$addressbooks:</key>
+ <value>$root:addressbooks/</value>
+ </substitution>
+
+ <!-- relative path to record type addressbook collections-->
+ <substitution>
+ <key>$addressbooks_uids:</key>
+ <value>$addressbooks:$uidstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$addressbooks_users:</key>
+ <value>$addressbooks:$userstype:/</value>
+ </substitution>
+ <substitution>
+ <key>$addressbooks_groups:</key>
+ <value>$addressbooks:$groupstype:/</value>
+ </substitution>
+
+ <!-- primary addressbook name -->
+ <substitution>
+ <key>$addressbook:</key>
+ <value>addressbook</value>
+ </substitution>
+
+ <!-- directory name -->
+ <substitution>
+ <key>$directory:</key>
+ <value>$root:directory/</value>
+ </substitution>
+
+ <!-- global-addressbook name -->
+ <substitution>
+ <key>$global_addressbook:</key>
+ <value>global-addressbook</value>
+ </substitution>
+
+ <!-- POST add-member URI suffix -->
+ <substitution>
+ <key>$add-member:</key>
+ <value>;add-member</value>
+ </substitution>
+
+ <!-- user id for admin user -->
+ <substitution>
+ <key>$useradmin:</key>
+ <value>admin</value>
+ </substitution>
+ <!-- guid for admin user -->
+ <substitution>
+ <key>$useradminguid:</key>
+ <value>admin</value>
+ </substitution>
+ <!-- password for admin user -->
+ <substitution>
+ <key>$pswdadmin:</key>
+ <value>admin</value>
+ </substitution>
+
+ <!-- relative path to admin principal resource-->
+ <substitution>
+ <key>$principal_admin:</key>
+ <value>$principals_users:$useradmin:/</value>
+ </substitution>
+ <substitution>
+ <key>$principaluri_admin:</key>
+ <value>$principals_uids:$useradminguid:/</value>
+ </substitution>
+
+ <!-- Forty podA user accounts -->
+ <repeat count="40">
+ <!-- user id -->
+ <substitution>
+ <key>$userid%d:</key>
+ <value>user%02d</value>
+ </substitution>
+ <!-- user guid -->
+ <substitution>
+ <key>$userguid%d:</key>
+ <value>user%02d</value>
+ </substitution>
+ <!-- user name -->
+ <substitution>
+ <key>$username%d:</key>
+ <value>User %02d</value>
+ </substitution>
+ <!-- user name URI encoded -->
+ <substitution>
+ <key>$username-encoded%d:</key>
+ <value>User%%20%02d</value>
+ </substitution>
+ <!-- first name -->
+ <substitution>
+ <key>$firstname%d:</key>
+ <value>User</value>
+ </substitution>
+ <!-- last name -->
+ <substitution>
+ <key>$lastname%d:</key>
+ <value>%02d</value>
+ </substitution>
+ <!-- password -->
+ <substitution>
+ <key>$pswd%d:</key>
+ <value>user%02d</value>
+ </substitution>
+ <!-- relative path to user principal resource-->
+ <substitution>
+ <key>$principal%d:</key>
+ <value>$principals_users:$userid%d:/</value>
+ </substitution>
+ <substitution>
+ <key>$principaluri%d:</key>
+ <value>$principals_uids:$userguid%d:/</value>
+ </substitution>
+ <substitution>
+ <key>$principal%dnoslash:</key>
+ <value>$principals_users:$userid%d:</value>
+ </substitution>
+
+ <!-- relative path to user calendar home-->
+ <substitution>
+ <key>$calendarhome%d:</key>
+ <value>$calendars_uids:$userguid%d:</value>
+ </substitution>
+ <!-- relative path to user alternate calendar home-->
+ <substitution>
+ <key>$calendarhomealt%d:</key>
+ <value>$calendars_users:$userid%d:</value>
+ </substitution>
+ <!-- relative path to user calendar-->
+ <substitution>
+ <key>$calendarpath%d:</key>
+ <value>$calendarhome%d:/$calendar:</value>
+ </substitution>
+ <!-- relative path to user alternate calendar-->
+ <substitution>
+ <key>$calendarpathalt%d:</key>
+ <value>$calendarhomealt%d:/$calendar:</value>
+ </substitution>
+ <!-- relative path to user tasks calendar-->
+ <substitution>
+ <key>$taskspath%d:</key>
+ <value>$calendarhome%d:/$tasks:</value>
+ </substitution>
+ <!-- relative path to user inbox-->
+ <substitution>
+ <key>$inboxpath%d:</key>
+ <value>$calendarhome%d:/$inbox:</value>
+ </substitution>
+ <!-- relative path to user outbox-->
+ <substitution>
+ <key>$outboxpath%d:</key>
+ <value>$calendarhome%d:/$outbox:</value>
+ </substitution>
+ <!-- relative path to user dropbox-->
+ <substitution>
+ <key>$dropboxpath%d:</key>
+ <value>$calendarhome%d:/$dropbox:</value>
+ </substitution>
+ <!-- relative path to user notification-->
+ <substitution>
+ <key>$notificationpath%d:</key>
+ <value>$calendarhome%d:/$notification:</value>
+ </substitution>
+ <!-- relative path to user freebusy-->
+ <substitution>
+ <key>$freebusypath%d:</key>
+ <value>$calendarhome%d:/$freebusy:</value>
+ </substitution>
+ <substitution>
+ <key>$email%d:</key>
+ <value>$userid%d:@example.com</value>
+ </substitution>
+ <!-- calendar user address of user-->
+ <substitution>
+ <key>$cuaddr%d:</key>
+ <value>mailto:$email%d:</value>
+ </substitution>
+ <substitution>
+ <key>$cuaddralt%d:</key>
+ <value>$principaluri%d:</value>
+ </substitution>
+ <substitution>
+ <key>$cuaddraltnoslash%d:</key>
+ <value>$principals_uids:$userguid%d:</value>
+ </substitution>
+ <substitution>
+ <key>$cuaddrurn%d:</key>
+ <value>urn:uuid:$userguid%d:</value>
+ </substitution>
+
+ <!-- relative path to user addressbook home-->
+ <substitution>
+ <key>$addressbookhome%d:</key>
+ <value>$addressbooks_uids:$userguid%d:</value>
+ </substitution>
+ <!-- relative path to user addressbook-->
+ <substitution>
+ <key>$addressbookpath%d:</key>
+ <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
+ </substitution>
+ </repeat>
+
+ <!-- Forty podB user accounts -->
+ <repeat count="40">
+ <!-- user id -->
+ <substitution>
+ <key>$puserid%d:</key>
+ <value>puser%02d</value>
+ </substitution>
+ <!-- user guid -->
+ <substitution>
+ <key>$puserguid%d:</key>
+ <value>puser%02d</value>
+ </substitution>
+ <!-- user name -->
+ <substitution>
+ <key>$pusername%d:</key>
+ <value>Puser %02d</value>
+ </substitution>
+ <!-- user name URI encoded -->
+ <substitution>
+ <key>$pusername-encoded%d:</key>
+ <value>Puser%%20%02d</value>
+ </substitution>
+ <!-- first name -->
+ <substitution>
+ <key>$pfirstname%d:</key>
+ <value>Puser</value>
+ </substitution>
+ <!-- last name -->
+ <substitution>
+ <key>$plastname%d:</key>
+ <value>%02d</value>
+ </substitution>
+ <!-- password -->
+ <substitution>
+ <key>$ppswd%d:</key>
+ <value>puser%02d</value>
+ </substitution>
+ <!-- relative path to user principal resource-->
+ <substitution>
+ <key>$pprincipal%d:</key>
+ <value>$principals_users:$puserid%d:/</value>
+ </substitution>
+ <substitution>
+ <key>$pprincipaluri%d:</key>
+ <value>$principals_uids:$puserguid%d:/</value>
+ </substitution>
+ <substitution>
+ <key>$pprincipal%dnoslash:</key>
+ <value>$principals_users:$puserid%d:</value>
+ </substitution>
+
+ <!-- relative path to user calendar home-->
+ <substitution>
+ <key>$pcalendarhome%d:</key>
+ <value>$calendars_uids:$puserguid%d:</value>
+ </substitution>
+ <!-- relative path to user alternate calendar home-->
+ <substitution>
+ <key>$pcalendarhomealt%d:</key>
+ <value>$calendars_users:$puserid%d:</value>
+ </substitution>
+ <!-- relative path to user calendar-->
+ <substitution>
+ <key>$pcalendarpath%d:</key>
+ <value>$pcalendarhome%d:/$calendar:</value>
+ </substitution>
+ <!-- relative path to user alternate calendar-->
+ <substitution>
+ <key>$pcalendarpathalt%d:</key>
+ <value>$pcalendarhomealt%d:/$calendar:</value>
+ </substitution>
+ <!-- relative path to user tasks calendar-->
+ <substitution>
+ <key>$ptaskspath%d:</key>
+ <value>$pcalendarhome%d:/$tasks:</value>
+ </substitution>
+ <!-- relative path to user inbox-->
+ <substitution>
+ <key>$pinboxpath%d:</key>
+ <value>$pcalendarhome%d:/$inbox:</value>
+ </substitution>
+ <!-- relative path to user outbox-->
+ <substitution>
+ <key>$poutboxpath%d:</key>
+ <value>$pcalendarhome%d:/$outbox:</value>
+ </substitution>
+ <!-- relative path to user dropbox-->
+ <substitution>
+ <key>$pdropboxpath%d:</key>
+ <value>$pcalendarhome%d:/$dropbox:</value>
+ </substitution>
+ <!-- relative path to user notification-->
+ <substitution>
+ <key>$pnotificationpath%d:</key>
+ <value>$pcalendarhome%d:/$notification:</value>
+ </substitution>
+ <!-- relative path to user freebusy-->
+ <substitution>
+ <key>$pfreebusypath%d:</key>
+ <value>$pcalendarhome%d:/$freebusy:</value>
+ </substitution>
+ <substitution>
+ <key>$pemail%d:</key>
+ <value>$puserid%d:@example.com</value>
+ </substitution>
+ <!-- calendar user address of user-->
+ <substitution>
+ <key>$pcuaddr%d:</key>
+ <value>mailto:$pemail%d:</value>
+ </substitution>
+ <substitution>
+ <key>$pcuaddralt%d:</key>
+ <value>$pprincipaluri%d:</value>
+ </substitution>
+ <substitution>
+ <key>$pcuaddraltnoslash%d:</key>
+ <value>$principals_uids:$puserguid%d:</value>
+ </substitution>
+ <substitution>
+ <key>$pcuaddrurn%d:</key>
+ <value>urn:uuid:$puserguid%d:</value>
+ </substitution>
+
+ <!-- relative path to user addressbook home-->
+ <substitution>
+ <key>$paddressbookhome%d:</key>
+ <value>$addressbooks_uids:$puserguid%d:</value>
+ </substitution>
+ <!-- relative path to user addressbook-->
+ <substitution>
+ <key>$paddressbookpath%d:</key>
+ <value>$paddressbookhome%d:/$addressbook:</value>
+ </substitution>
+ </repeat>
+
+ </substitutions>
+</serverinfo>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -79,8 +79,8 @@
<feature>managed-attachments</feature> <!-- CalDAV Managed Attachments -->
<feature>maskuid</feature> <!-- maskuid extension -->
<feature>no-duplicate-uids</feature> <!-- duplicate UIDs in same home not supported -->
- <!-- <feature>partitioning</feature> --> <!-- Partitioned server -->
<feature>partstat-timestamp</feature> <!-- Time stamps when PARTSTAT changes extension -->
+ <!-- <feature>podding</feature> --> <!-- Podded server -->
<feature>private-comments</feature> <!-- private-comments extension -->
<feature>private-events</feature> <!-- private-events extension -->
<feature>proxy</feature> <!-- calendar-user-proxy extension -->
@@ -98,13 +98,14 @@
<feature>timezone-service</feature> <!-- Timezone service extension for Wiki -->
<feature>timezone-std-service</feature> <!-- Timezone standard service extension -->
<feature>vavailability</feature> <!-- VAVAILABILITY on inbox -->
+ <!-- <feature>vpoll</feature> --> <!-- VPOLL support for store and scheduling -->
<feature>webcal</feature> <!-- Internet calendar subscription via GET on calendar collection -->
<!-- CardDAV specific extension -->
<feature>carddav</feature> <!-- Basic CardDAV feature enabler -->
<feature>default-addressbook</feature> <!-- Default address book behavior -->
<!-- <feature>global-addressbook</feature> --> <!-- Global address book for each user -->
- <!-- <feature>shared-addressbooks</feature> --> <!-- Shared address books extension -->
+ <!-- <feature>shared-addressbooks</feature> --> <!-- Shared address books extension -->
<!-- <feature>directory-gateway</feature> --> <!-- Directory gateway extension -->
</features>
@@ -539,7 +540,7 @@
<!-- relative path to user addressbook-->
<substitution>
<key>$addressbookpath%%d:</key>
- <value>$addressbooks_uids:$userguid%%d:/$addressbook:</value>
+ <value>$addressbookhome%%d:/$addressbook:</value>
</substitution>
</repeat>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,11 +14,14 @@
limitations under the License.
-->
-<!ELEMENT serverinfo (host, nonsslport, sslport, authtype?, waitime?, features?, substitutions)? >
+<!ELEMENT serverinfo (host, nonsslport, sslport, host2?, nonsslport2?, sslport2?, authtype?, waitime?, features?, substitutions)? >
<!ELEMENT host (#PCDATA)>
<!ELEMENT nonsslport (#PCDATA)>
<!ELEMENT sslport (#PCDATA)>
+ <!ELEMENT host2 (#PCDATA)>
+ <!ELEMENT nonsslport2 (#PCDATA)>
+ <!ELEMENT sslport2 (#PCDATA)>
<!ELEMENT authtype (#PCDATA)>
<!ELEMENT waittime (#PCDATA)>
<!ELEMENT features (feature*)>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -78,9 +78,9 @@
<feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
<feature>managed-attachments</feature> <!-- CalDAV Managed Attachments -->
<feature>maskuid</feature> <!-- maskuid extension -->
- <!-- <feature>partitioning</feature> --> <!-- Partitioned server -->
<feature>no-duplicate-uids</feature> <!-- duplicate UIDs in same home not supported -->
<feature>partstat-timestamp</feature> <!-- Time stamps when PARTSTAT changes extension -->
+ <!-- <feature>podding</feature> --> <!-- Podded server -->
<feature>private-comments</feature> <!-- private-comments extension -->
<feature>private-events</feature> <!-- private-events extension -->
<feature>proxy</feature> <!-- calendar-user-proxy extension -->
@@ -98,6 +98,7 @@
<feature>timezone-service</feature> <!-- Timezone service extension for Wiki -->
<feature>timezone-std-service</feature> <!-- Timezone standard service extension -->
<feature>vavailability</feature> <!-- VAVAILABILITY on inbox -->
+ <!-- <feature>vpoll</feature> --> <!-- VPOLL support for store and scheduling -->
<feature>webcal</feature> <!-- Internet calendar subscription via GET on calendar collection -->
<!-- CardDAV specific extension -->
@@ -248,6 +249,12 @@
<value>tasks</value>
</substitution>
+ <!-- primary polls-only calendar name-->
+ <substitution>
+ <key>$polls:</key>
+ <value>polls</value>
+ </substitution>
+
<!-- inbox name-->
<substitution>
<key>$inbox:</key>
@@ -302,13 +309,13 @@
<value>$root:stdtimezones</value>
</substitution>
- <!-- relative path to calendars collection-->
+ <!-- relative path to addressbooks collection-->
<substitution>
<key>$addressbooks:</key>
<value>$root:addressbooks/</value>
</substitution>
- <!-- relative path to record type calendar collections-->
+ <!-- relative path to record type addressbook collections-->
<substitution>
<key>$addressbooks_uids:</key>
<value>$addressbooks:$uidstype:/</value>
@@ -485,6 +492,11 @@
<key>$taskspath%d:</key>
<value>$calendarhome%d:/$tasks:</value>
</substitution>
+ <!-- relative path to user polls calendar-->
+ <substitution>
+ <key>$pollspath%d:</key>
+ <value>$calendarhome%d:/$polls:</value>
+ </substitution>
<!-- relative path to user inbox-->
<substitution>
<key>$inboxpath%d:</key>
@@ -540,7 +552,7 @@
<!-- relative path to user addressbook-->
<substitution>
<key>$addressbookpath%d:</key>
- <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
+ <value>$addressbookhome%d:/$addressbook:</value>
</substitution>
</repeat>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,7 +31,8 @@
<!ELEMENT request (require-feature?, exclude-feature?, method, ruri*, header*, data?, verify*,
graburi?, grabcount?, grabheader*, grabproperty*, grabelement*, grabcalproperty*, grabcalparameter*)>
- <!ATTLIST request auth (yes|no) "yes"
+ <!ATTLIST request host2 (yes|no) "no"
+ auth (yes|no) "yes"
user CDATA ""
pswd CDATA ""
end-delete (yes|no) "no"
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -357,7 +357,7 @@
</arg>
<arg>
<name>nohrefs</name>
- <value>events/</value>
+ <value>$calendar:/</value>
</arg>
</verify>
</request>
@@ -424,7 +424,7 @@
</arg>
<arg>
<name>okhrefs</name>
- <value>events/</value>
+ <value>$calendar:/</value>
</arg>
</verify>
</request>
@@ -433,7 +433,7 @@
<description>Attendee has data</description>
<request user="$userid10:" pswd="$pswd10:" print-response='no'>
<method>GETNEW</method>
- <ruri>$calendarhome10:/events/</ruri>
+ <ruri>$calendarpath10:/</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
@@ -451,7 +451,7 @@
</request>
<request user="$userid10:" pswd="$pswd10:">
<method>DELETEALL</method>
- <ruri>$calendarhome10:/events/</ruri>
+ <ruri>$calendarpath10:/</ruri>
</request>
<request user="$userid1:" pswd="$pswd1:">
<method>DELETEALL</method>
@@ -461,14 +461,6 @@
<method>DELETEALL</method>
<ruri>$inboxpath10:/</ruri>
</request>
- <request user="$userid10:" pswd="$pswd10:" print-response='no'>
- <method>MKCALENDAR</method>
- <ruri>$calendarpath10:/</ruri>
- </request>
- <request user="$userid10:" pswd="$pswd10:">
- <method>DELETE</method>
- <ruri>$calendarhome10:/events/</ruri>
- </request>
</test>
</test-suite>
Copied: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml (from rev 12016, CalDAVTester/trunk/scripts/tests/CalDAV/polls.xml)
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml (rev 0)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,171 @@
+<?xml version="1.0" standalone="no"?>
+
+<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd">
+
+<!--
+ Copyright (c) 2006-2013 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.
+ -->
+
+<caldavtest>
+ <description>Test PUT method in CalDAV</description>
+
+ <require-feature>
+ <feature>caldav</feature>
+ <feature>vpoll</feature>
+ </require-feature>
+
+ <start/>
+
+ <test-suite name='supported-component-set'>
+ <test name='1'>
+ <require-feature>
+ <feature>supported-component-sets</feature>
+ </require-feature>
+ <description>Check calendar home for property</description>
+ <request>
+ <method>PROPFIND</method>
+ <ruri>$calendarhome1:/</ruri>
+ <header>
+ <name>Depth</name>
+ <value>0</value>
+ </header>
+ <data>
+ <content-type>text/xml; charset=utf-8</content-type>
+ <filepath>Resource/CalDAV/polls/supported-component-set/1.xml</filepath>
+ </data>
+ <verify>
+ <exclude-feature>
+ <feature>supported-component-sets-one</feature>
+ </exclude-feature>
+ <callback>propfindItems</callback>
+ <arg>
+ <name>okprops</name>
+ <value>{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets$</value>
+ </arg>
+ </verify>
+ <verify>
+ <require-feature>
+ <feature>supported-component-sets-one</feature>
+ </require-feature>
+ <callback>xmlElementMatch</callback>
+ <arg>
+ <name>exists</name>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+ </arg>
+ </verify>
+ </request>
+ </test>
+ <test name='2'>
+ <description>Check calendar for property</description>
+ <request>
+ <method>PROPFIND</method>
+ <ruri>$pollspath1:/</ruri>
+ <header>
+ <name>Depth</name>
+ <value>0</value>
+ </header>
+ <data>
+ <content-type>text/xml; charset=utf-8</content-type>
+ <filepath>Resource/CalDAV/polls/supported-component-set/2.xml</filepath>
+ </data>
+ <verify>
+ <require-feature>
+ <feature>exclude-calendars</feature>
+ </require-feature>
+ <callback>xmlElementMatch</callback>
+ <arg>
+ <name>exists</name>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+ </arg>
+ </verify>
+ <verify>
+ <require-feature>
+ <feature>split-calendars</feature>
+ </require-feature>
+ <callback>xmlElementMatch</callback>
+ <arg>
+ <name>exists</name>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+ </arg>
+ <arg>
+ <name>notexists</name>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+ <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+ </arg>
+ </verify>
+ </request>
+ </test>
+ </test-suite>
+
+ <test-suite name='PUT VPOLL' ignore='no'>
+ <test name='1' ignore='no'>
+ <description>PUT unscheduled empty poll</description>
+ <request end-delete='yes'>
+ <method>PUT</method>
+ <ruri>$pollspath1:/1.ics</ruri>
+ <data>
+ <content-type>text/calendar; charset=utf-8</content-type>
+ <filepath>Resource/CalDAV/polls/put/1.ics</filepath>
+ </data>
+ <verify>
+ <callback>statusCode</callback>
+ </verify>
+ </request>
+ <request>
+ <method>GET</method>
+ <ruri>$pollspath1:/1.ics</ruri>
+ <verify>
+ <callback>calendarDataMatch</callback>
+ <arg>
+ <name>filepath</name>
+ <value>Resource/CalDAV/polls/put/2.ics</value>
+ </arg>
+ </verify>
+ </request>
+ </test>
+ <test name='2' ignore='no'>
+ <description>PUT unscheduled event poll</description>
+ <request end-delete='yes'>
+ <method>PUT</method>
+ <ruri>$pollspath1:/2.ics</ruri>
+ <data>
+ <content-type>text/calendar; charset=utf-8</content-type>
+ <filepath>Resource/CalDAV/polls/put/3.ics</filepath>
+ </data>
+ <verify>
+ <callback>statusCode</callback>
+ </verify>
+ </request>
+ <request>
+ <method>GET</method>
+ <ruri>$pollspath1:/2.ics</ruri>
+ <verify>
+ <callback>calendarDataMatch</callback>
+ <arg>
+ <name>filepath</name>
+ <value>Resource/CalDAV/polls/put/4.ics</value>
+ </arg>
+ </verify>
+ </request>
+ </test>
+ </test-suite>
+
+ <end/>
+
+</caldavtest>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1006,55 +1006,34 @@
</request>
</test>
<test name='8'>
- <description>Attendee removes comment property entirely</description>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
- <method>GETNEW</method>
- <ruri>$calendarpath2:/</ruri>
- <verify>
- <callback>statusCode</callback>
- </verify>
+ <description>Organizer Inbox Item</description>
+ <request print-response='no'>
+ <method>WAITDELETEALL 1</method>
+ <ruri>$inboxpath1:/</ruri>
</request>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
- <method>PUT</method>
- <ruri>$</ruri>
- <data>
- <content-type>text/calendar; charset=utf-8</content-type>
- <filepath>Resource/CalDAV/privatecomments/31.ics</filepath>
- </data>
- <verify>
- <callback>statusCode</callback>
- </verify>
- </request>
</test>
<test name='9'>
- <description>Attendee has data with comment preserved</description>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
- <method>GETNEW</method>
- <ruri>$calendarpath2:/</ruri>
+ <description>Organizer data changed</description>
+ <request print-response='no'>
+ <method>GET</method>
+ <ruri>$calendarpath1:/3.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
- <value>Resource/CalDAV/privatecomments/32.ics</value>
+ <value>Resource/CalDAV/privatecomments/35.ics</value>
</arg>
</verify>
</request>
</test>
<test name='10'>
- <description>Attendee changes comment</description>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
- <method>GETNEW</method>
- <ruri>$calendarpath2:/</ruri>
- <verify>
- <callback>statusCode</callback>
- </verify>
- </request>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
+ <description>Organizer removes comments entirely</description>
+ <request print-response='no'>
<method>PUT</method>
- <ruri>$</ruri>
+ <ruri>$calendarpath1:/3.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
- <filepath>Resource/CalDAV/privatecomments/33.ics</filepath>
+ <filepath>Resource/CalDAV/privatecomments/36.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
@@ -1062,55 +1041,34 @@
</request>
</test>
<test name='11'>
- <description>Attendee has data with comment preserved</description>
- <request user="$userid2:" pswd="$pswd2:" print-response='no'>
- <method>GETNEW</method>
- <ruri>$calendarpath2:/</ruri>
- <verify>
- <callback>calendarDataMatch</callback>
- <arg>
- <name>filepath</name>
- <value>Resource/CalDAV/privatecomments/34.ics</value>
- </arg>
- </verify>
- </request>
- </test>
- <test name='12'>
- <description>Organizer Inbox Items</description>
+ <description>Organizer checks data</description>
<request print-response='no'>
- <method>WAITDELETEALL 2</method>
- <ruri>$inboxpath1:/</ruri>
- </request>
- </test>
- <test name='13'>
- <description>Organizer data changed</description>
- <request print-response='no'>
<method>GET</method>
<ruri>$calendarpath1:/3.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
- <value>Resource/CalDAV/privatecomments/35.ics</value>
+ <value>Resource/CalDAV/privatecomments/37.ics</value>
</arg>
</verify>
</request>
</test>
- <test name='15'>
- <description>Organizer removes comments entirely</description>
+ <test name='12'>
+ <description>Organizer changes comment</description>
<request print-response='no'>
<method>PUT</method>
<ruri>$calendarpath1:/3.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
- <filepath>Resource/CalDAV/privatecomments/36.ics</filepath>
+ <filepath>Resource/CalDAV/privatecomments/38.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
- <test name='16'>
+ <test name='13'>
<description>Organizer checks data</description>
<request print-response='no'>
<method>GET</method>
@@ -1119,35 +1077,49 @@
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
- <value>Resource/CalDAV/privatecomments/37.ics</value>
+ <value>Resource/CalDAV/privatecomments/38.ics</value>
</arg>
</verify>
</request>
</test>
- <test name='17'>
- <description>Organizer changes comment</description>
- <request print-response='no'>
+ <test name='14'>
+ <description>Attendee removes comment property entirely</description>
+ <request user="$userid2:" pswd="$pswd2:" print-response='no'>
+ <method>GETNEW</method>
+ <ruri>$calendarpath2:/</ruri>
+ <verify>
+ <callback>statusCode</callback>
+ </verify>
+ </request>
+ <request user="$userid2:" pswd="$pswd2:" print-response='no'>
<method>PUT</method>
- <ruri>$calendarpath1:/3.ics</ruri>
+ <ruri>$</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
- <filepath>Resource/CalDAV/privatecomments/38.ics</filepath>
+ <filepath>Resource/CalDAV/privatecomments/31.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
- <test name='18'>
- <description>Organizer checks data</description>
+ <test name='15'>
+ <description>Organizer Inbox Item</description>
<request print-response='no'>
+ <method>WAITDELETEALL 1</method>
+ <ruri>$inboxpath1:/</ruri>
+ </request>
+ </test>
+ <test name='16'>
+ <description>Organizer data changed</description>
+ <request print-response='no'>
<method>GET</method>
<ruri>$calendarpath1:/3.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
- <value>Resource/CalDAV/privatecomments/38.ics</value>
+ <value>Resource/CalDAV/privatecomments/32.ics</value>
</arg>
</verify>
</request>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -239,36 +239,12 @@
<filepath>Resource/CalDAV/sharing/notification-sync/2.xml</filepath>
</data>
<verify>
- <exclude-feature>
- <feature>split-calendars</feature>
- </exclude-feature>
<callback>multistatusItems</callback>
<arg>
- <name>okhrefs</name>
- <value>$calendar:/</value>
- <value>$inbox:/</value>
- <value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
- <value>shared/</value>
+ <name>ignoremissing</name>
</arg>
<arg>
- <name>badhrefs</name>
- <value>$dropbox:/</value>
- </arg>
- </verify>
- <verify>
- <require-feature>
- <feature>split-calendars</feature>
- </require-feature>
- <callback>multistatusItems</callback>
- <arg>
<name>okhrefs</name>
- <value>$calendar:/</value>
- <value>$tasks:/</value>
- <value>$inbox:/</value>
- <value>$outbox:/</value>
- <value>$freebusy:</value>
<value>$notification:/</value>
<value>shared/</value>
</arg>
@@ -300,35 +276,12 @@
<filepath>Resource/CalDAV/sharing/notification-sync/2.xml</filepath>
</data>
<verify>
- <exclude-feature>
- <feature>split-calendars</feature>
- </exclude-feature>
<callback>multistatusItems</callback>
<arg>
- <name>okhrefs</name>
- <value>$calendar:/</value>
- <value>$inbox:/</value>
- <value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
+ <name>ignoremissing</name>
</arg>
<arg>
- <name>badhrefs</name>
- <value>$dropbox:/</value>
- </arg>
- </verify>
- <verify>
- <require-feature>
- <feature>split-calendars</feature>
- </require-feature>
- <callback>multistatusItems</callback>
- <arg>
<name>okhrefs</name>
- <value>$calendar:/</value>
- <value>$tasks:/</value>
- <value>$inbox:/</value>
- <value>$outbox:/</value>
- <value>$freebusy:</value>
<value>$notification:/</value>
</arg>
<arg>
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -262,12 +262,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -282,13 +283,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -321,12 +323,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -341,13 +344,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -380,12 +384,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -400,13 +405,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -439,12 +445,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -463,13 +470,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -506,12 +514,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -530,13 +539,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -573,12 +583,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -597,13 +608,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -841,12 +853,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -861,13 +874,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -900,12 +914,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -924,13 +939,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1005,12 +1021,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1025,13 +1042,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1064,12 +1082,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1089,13 +1108,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1166,12 +1186,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1186,13 +1207,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1225,12 +1247,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1249,13 +1272,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1329,12 +1353,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1349,13 +1374,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar2/</value>
</arg>
@@ -1388,12 +1414,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -1412,13 +1439,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar1/</value>
<value>synccalendar1/1.ics</value>
<value>synccalendar1/2.ics</value>
@@ -2063,12 +2091,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2087,13 +2116,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2298,12 +2328,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2322,13 +2353,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2579,12 +2611,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2603,13 +2636,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar3/1.ics</value>
<value>synccalendar3/2.ics</value>
@@ -2795,12 +2829,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -2815,13 +2850,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3012,12 +3048,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3032,13 +3069,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3275,12 +3313,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3295,13 +3334,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3481,12 +3521,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
<value>synccalendar4/1.ics</value>
@@ -3503,13 +3544,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
<value>synccalendar4/1.ics</value>
@@ -3639,12 +3681,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3659,13 +3702,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3794,12 +3838,13 @@
</exclude-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
@@ -3814,13 +3859,14 @@
</require-feature>
<callback>multistatusItems</callback>
<arg>
+ <name>ignoremissing</name>
+ </arg>
+ <arg>
<name>okhrefs</name>
<value>$calendar:/</value>
<value>$tasks:/</value>
<value>$inbox:/</value>
<value>$outbox:/</value>
- <value>$freebusy:</value>
- <value>$notification:/</value>
<value>synccalendar3/</value>
<value>synccalendar4/</value>
</arg>
Property changes on: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV
___________________________________________________________________
Modified: svn:mergeinfo
- /CalDAVTester/branches/release/CalDAVTester-3.0-dev/scripts/tests/CardDAV:7584
/CalDAVTester/branches/release/CalDAVTester-4.3-dev/scripts/tests/CardDAV:10193
/CalDAVTester/branches/users/cdaboo/attendee-comments-2887/scripts/tests/CardDAV:2888-2910
/CalDAVTester/branches/users/cdaboo/better-proxy-3148/scripts/tests/CardDAV:3149-3163
/CalDAVTester/branches/users/cdaboo/component-set-fixes/scripts/tests/CardDAV:8221-8346
/CalDAVTester/branches/users/cdaboo/conditional-4466/scripts/tests/CardDAV:4467-4469
/CalDAVTester/branches/users/cdaboo/implicitauto-2948/scripts/tests/CardDAV:2949-2989
/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574/scripts/tests/CardDAV:3575-3581
/CalDAVTester/branches/users/cdaboo/managed-attachments/scripts/tests/CardDAV:9986-10145
/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533/scripts/tests/CardDAV:3534-3558
/CalDAVTester/branches/users/cdaboo/pycalendar/scripts/tests/CardDAV:7160-7206
/CalDAVTester/branches/users/cdaboo/pycard/scripts/tests/CardDAV:7226-7237
/CalDAVTester/branches/users/cdaboo/sharing-5228/scripts/tests/CardDAV:5229-5440
/CalDAVTester/branches/users/gaya/sharedgroupstester-2/scripts/tests/CardDAV:11078-11181
/CalDAVTester/branches/users/gaya/sharedgroupstester-3/scripts/tests/CardDAV:11181-11204
/CalDAVTester/trunk/scripts/tests/CardDAV:11742-11861
+ /CalDAVTester/branches/release/CalDAVTester-3.0-dev/scripts/tests/CardDAV:7584
/CalDAVTester/branches/release/CalDAVTester-4.3-dev/scripts/tests/CardDAV:10193
/CalDAVTester/branches/users/cdaboo/attendee-comments-2887/scripts/tests/CardDAV:2888-2910
/CalDAVTester/branches/users/cdaboo/better-proxy-3148/scripts/tests/CardDAV:3149-3163
/CalDAVTester/branches/users/cdaboo/component-set-fixes/scripts/tests/CardDAV:8221-8346
/CalDAVTester/branches/users/cdaboo/conditional-4466/scripts/tests/CardDAV:4467-4469
/CalDAVTester/branches/users/cdaboo/implicitauto-2948/scripts/tests/CardDAV:2949-2989
/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574/scripts/tests/CardDAV:3575-3581
/CalDAVTester/branches/users/cdaboo/managed-attachments/scripts/tests/CardDAV:9986-10145
/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533/scripts/tests/CardDAV:3534-3558
/CalDAVTester/branches/users/cdaboo/pycalendar/scripts/tests/CardDAV:7160-7206
/CalDAVTester/branches/users/cdaboo/pycard/scripts/tests/CardDAV:7226-7237
/CalDAVTester/branches/users/cdaboo/sharing-5228/scripts/tests/CardDAV:5229-5440
/CalDAVTester/branches/users/gaya/sharedgroupstester-2/scripts/tests/CardDAV:11078-11181
/CalDAVTester/branches/users/gaya/sharedgroupstester-3/scripts/tests/CardDAV:11181-11204
/CalDAVTester/trunk/scripts/tests/CardDAV:11742-12016
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,7 +31,8 @@
<!ELEMENT request (require-feature?, exclude-feature?, method, ruri*, header*, data?, verify*,
graburi?, grabcount?, grabheader*, grabproperty*, grabelement*, grabcalproperty*, grabcalparameter*)>
- <!ATTLIST request auth (yes|no) "yes"
+ <!ATTLIST request host2 (yes|no) "no"
+ auth (yes|no) "yes"
user CDATA ""
pswd CDATA ""
end-delete (yes|no) "no"
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,7 +19,7 @@
"""
from cStringIO import StringIO
-from pycalendar.calendar import PyCalendar
+from pycalendar.icalendar.calendar import Calendar
from src.httpshandler import SmartHTTPConnection
from src.manager import manager
from src.request import data, pause
@@ -583,7 +583,7 @@
stats.startTimer()
# Do the http request
- http = SmartHTTPConnection(self.manager.server_info.host, self.manager.server_info.port, self.manager.server_info.ssl)
+ http = SmartHTTPConnection(req.host, req.port, self.manager.server_info.ssl)
if not 'User-Agent' in headers and label is not None:
headers['User-Agent'] = label.encode("utf-8")
@@ -916,7 +916,7 @@
prop = self._calProperty(propertyname, respdata)
try:
- return prop.getAttributeValue(pname) if prop else None
+ return prop.getParameterValue(pname) if prop else None
except KeyError:
return None
@@ -924,7 +924,7 @@
def _calProperty(self, propertyname, respdata):
try:
- cal = PyCalendar.parseText(respdata)
+ cal = Calendar.parseText(respdata)
except Exception:
return None
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -100,12 +100,22 @@
# Setup ssl stuff
self.server_info.ssl = ssl
self.server_info.port = self.server_info.sslport if ssl else self.server_info.nonsslport
+ self.server_info.port2 = self.server_info.sslport2 if ssl else self.server_info.nonsslport2
+
moresubs["$host:"] = "%s://%s:%d" % (
"https" if ssl else "http",
self.server_info.host,
self.server_info.port,
)
moresubs["$hostssl:"] = "https://%s:%d" % (self.server_info.host, self.server_info.sslport,)
+
+ moresubs["$host2:"] = "%s://%s:%d" % (
+ "https" if ssl else "http",
+ self.server_info.host2,
+ self.server_info.port2,
+ )
+ moresubs["$hostssl2:"] = "https://%s:%d" % (self.server_info.host2, self.server_info.sslport2,)
+
self.server_info.addsubs(moresubs)
for testfile in testfiles:
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -142,6 +142,8 @@
def __init__(self, manager):
self.manager = manager
+ self.host = self.manager.server_info.host
+ self.port = self.manager.server_info.port
self.auth = True
self.user = ""
self.pswd = ""
@@ -345,6 +347,10 @@
self.iterate_data = getYesNoAttributeValue(node, src.xmlDefs.ATTR_ITERATE_DATA)
self.wait_for_success = getYesNoAttributeValue(node, src.xmlDefs.ATTR_WAIT_FOR_SUCCESS)
+ if node.get(src.xmlDefs.ATTR_HOST2, src.xmlDefs.ATTR_VALUE_NO) == src.xmlDefs.ATTR_VALUE_YES:
+ self.host = self.manager.server_info.host2
+ self.port = self.manager.server_info.port2
+
for child in node.getchildren():
if child.tag == src.xmlDefs.ELEMENT_REQUIRE_FEATURE:
self.parseFeatures(child, require=True)
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,6 +30,9 @@
self.host = ""
self.nonsslport = 80
self.sslport = 443
+ self.host2 = ""
+ self.nonsslport2 = 80
+ self.sslport2 = 443
self.authtype = "basic"
self.features = set()
self.user = ""
@@ -131,6 +134,15 @@
self.nonsslport = int(child.text)
elif child.tag == src.xmlDefs.ELEMENT_SSLPORT:
self.sslport = int(child.text)
+ elif child.tag == src.xmlDefs.ELEMENT_HOST2:
+ try:
+ self.host2 = child.text.encode("utf-8")
+ except:
+ self.host2 = "localhost"
+ elif child.tag == src.xmlDefs.ELEMENT_NONSSLPORT2:
+ self.nonsslport2 = int(child.text)
+ elif child.tag == src.xmlDefs.ELEMENT_SSLPORT2:
+ self.sslport2 = int(child.text)
elif child.tag == src.xmlDefs.ELEMENT_AUTHTYPE:
self.authtype = child.text.encode("utf-8")
elif child.tag == src.xmlDefs.ELEMENT_WAITTIME:
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -39,6 +39,7 @@
ELEMENT_GRABURI = "graburi"
ELEMENT_HEADER = "header"
ELEMENT_HOST = "host"
+ELEMENT_HOST2 = "host2"
ELEMENT_KEY = "key"
ELEMENT_LOGGING = "logging"
ELEMENT_MAILFROM = "mailfrom"
@@ -46,6 +47,7 @@
ELEMENT_METHOD = "method"
ELEMENT_NAME = "name"
ELEMENT_NONSSLPORT = "nonsslport"
+ELEMENT_NONSSLPORT2 = "nonsslport2"
ELEMENT_NOTIFY = "notify"
ELEMENT_PARENT = "parent"
ELEMENT_PAUSE = "pause"
@@ -59,6 +61,7 @@
ELEMENT_SERVERINFO = "serverinfo"
ELEMENT_SPREAD = "spread"
ELEMENT_SSLPORT = "sslport"
+ELEMENT_SSLPORT2 = "sslport2"
ELEMENT_START = "start"
ELEMENT_SUBJECT = "subject"
ELEMENT_SUBSTITUTIONS = "substitutions"
@@ -75,6 +78,7 @@
ELEMENT_WAITTIME = "waittime"
ELEMENT_WARNINGTIME = "warningtime"
+ATTR_HOST2 = "host2"
ATTR_AUTH = "auth"
ATTR_COUNT = "count"
ATTR_DETAILS = "details"
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -73,8 +73,8 @@
if ":" in filter:
propname, parameter = filter.split(":")
if property.getName() == propname:
- if property.hasAttribute(parameter):
- property.removeAttributes(parameter)
+ if property.hasParameter(parameter):
+ property.removeParameters(parameter)
else:
if property.getName() == filter:
component.removeProperty(property)
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,8 +15,8 @@
##
from difflib import unified_diff
-from pycalendar.calendar import PyCalendar
-from pycalendar.attribute import PyCalendarAttribute
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.parameter import Parameter
"""
Verifier that checks the response body for a semantic match to data in a file.
@@ -99,25 +99,25 @@
for property in allProps:
# Always reset DTSTAMP on these properties
if property.getName() in ("ATTENDEE", "X-CALENDARSERVER-ATTENDEE-COMMENT"):
- if property.hasAttribute("X-CALENDARSERVER-DTSTAMP"):
- property.replaceAttribute(PyCalendarAttribute("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))
+ if property.hasParameter("X-CALENDARSERVER-DTSTAMP"):
+ property.replaceParameter(Parameter("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))
for filter in filters:
if ":" in filter:
propname, parameter = filter.split(":")
if property.getName() == propname:
- if property.hasAttribute(parameter):
- property.removeAttributes(parameter)
+ if property.hasParameter(parameter):
+ property.removeParameters(parameter)
else:
if property.getName() == filter:
component.removeProperty(property)
try:
- resp_calendar = PyCalendar.parseText(respdata)
+ resp_calendar = Calendar.parseText(respdata)
removePropertiesParameters(resp_calendar)
respdata = resp_calendar.getText()
- data_calendar = PyCalendar.parseText(data)
+ data_calendar = Calendar.parseText(data)
removePropertiesParameters(data_calendar)
data = data_calendar.getText()
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,8 +18,8 @@
Verifier that checks the response of a free-busy-query.
"""
-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarInvalidData
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.exceptions import InvalidData
class Verifier(object):
@@ -36,7 +36,7 @@
# Parse data as calendar object
try:
- calendar = PyCalendar.parseText(respdata)
+ calendar = Calendar.parseText(respdata)
# Check for calendar
if calendar is None:
@@ -61,8 +61,8 @@
periods[i].getValue().setUseDuration(False)
# Check param
fbtype = "BUSY"
- if fp.hasAttribute("FBTYPE"):
- fbtype = fp.getAttributeValue("FBTYPE")
+ if fp.hasParameter("FBTYPE"):
+ fbtype = fp.getParameterValue("FBTYPE")
if fbtype == "BUSY":
busyp.extend(periods)
elif fbtype == "BUSY-TENTATIVE":
@@ -97,7 +97,7 @@
elif len(unavailablep.symmetric_difference(unavailable)):
raise ValueError("Busy-unavailable periods do not match")
- except PyCalendarInvalidData:
+ except InvalidData:
return False, " HTTP response data is not a calendar"
except ValueError, txt:
return False, " HTTP response data is invalid: %s" % (txt,)
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,8 +18,8 @@
Verifier that checks the response of a free-busy-query.
"""
-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarInvalidData
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.exceptions import InvalidData
from xml.etree.ElementTree import ElementTree
from xml.parsers.expat import ExpatError
import StringIO
@@ -47,7 +47,7 @@
for calendar in tree.findall("./{urn:ietf:params:xml:ns:caldav}response/{urn:ietf:params:xml:ns:caldav}calendar-data"):
# Parse data as calendar object
try:
- calendar = PyCalendar.parseText(calendar.text)
+ calendar = Calendar.parseText(calendar.text)
# Check for calendar
if calendar is None:
@@ -80,8 +80,8 @@
periods[i].getValue().setUseDuration(False)
# Check param
fbtype = "BUSY"
- if fp.hasAttribute("FBTYPE"):
- fbtype = fp.getAttributeValue("FBTYPE")
+ if fp.hasParameter("FBTYPE"):
+ fbtype = fp.getParameterValue("FBTYPE")
if fbtype == "BUSY":
busyp.extend(periods)
elif fbtype == "BUSY-TENTATIVE":
@@ -118,7 +118,7 @@
break
- except PyCalendarInvalidData:
+ except InvalidData:
return False, " HTTP response data is not a calendar"
except ValueError, txt:
return False, " HTTP response data is invalid: %s" % (txt,)
Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,7 +19,7 @@
Verifier that checks the response body for an exact match to data in a file.
"""
-from pycalendar.calendar import PyCalendar
+from pycalendar.icalendar.calendar import Calendar
from xml.etree.ElementTree import ElementTree
import json
import StringIO
@@ -141,6 +141,9 @@
for child in node.getchildren():
if child.tag == element and (value is None or child.text == value):
results.append(node)
+ elif test[0] == '|':
+ if node.text is None and len(node.getchildren()) == 0:
+ results.append(node)
else:
results = nodes
@@ -222,7 +225,7 @@
# Try to parse as iCalendar
elif test == 'icalendar':
try:
- PyCalendar.parseText(node.text)
+ Calendar.parseText(node.text)
except:
result = " Incorrect value returned in iCalendar for %s\n" % (path,)
Property changes on: CalendarServer/branches/users/gaya/sharedgroupfixes
___________________________________________________________________
Modified: svn:mergeinfo
- /CalDAVTester/trunk:11193-11198
/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/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
/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/fix-no-ischedule:11612
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
/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/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
/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/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
/CalendarServer/branches/users/glyph/hang-fix:11465-11491
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
/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/one-home-list-api:10048-10073
/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/queue-locking-and-timing:10204-10289
/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/start-service-start-loop:11060-11065
/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/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
/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/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:11632-11860
+ /CalDAVTester/trunk:11193-11198
/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/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
/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/fix-no-ischedule:11607-11871
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/json:11622-11912
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
/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/reverse-proxy-pods:11875-11900
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
/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/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
/CalendarServer/branches/users/glyph/hang-fix:11465-11491
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
/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/one-home-list-api:10048-10073
/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/queue-locking-and-timing:10204-10289
/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/start-service-start-loop:11060-11065
/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/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
/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/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:11632-11860,11862-12016
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING 2013-12-14 06:28:16 UTC (rev 12110)
@@ -144,10 +144,6 @@
PEP-8 items we do not follow:
- * Lines need not be limited to 79 spaces, but longer lines are
- undesirable. If you can easily do so, try to keep lines under 80
- columns.
-
* PEP-8 recommends using a backslash to break long lines up:
::
@@ -181,14 +177,6 @@
Because that's just silly.
- * Lining up assignments is OK, within reason:
-
- ::
-
- cars = 4
- motorbikes = 8
- bicycles = 18
-
Additions:
* Close parentheses and brackets such as ``()``, ``[]`` and ``{}`` at the
@@ -248,9 +236,8 @@
process = subprocess.Popen(...)
- This makes code shorter and removes the runtime indirection (which
- can be relevant in tight loops). It also makes it easier to replace
- one implementation with another.
+ This makes code shorter and makes it easier to replace one implementation
+ with another.
* All files should have an ``__all__`` specification. Put them at the
top of the file, before imports (PEP-8 puts them at the top, but
@@ -259,8 +246,8 @@
* It is more important that symbol names are meaningful than it is
that they be concise. ``x`` is rarely an appropriate name for a
- variable. ``transmogrifierStatus`` is more useful to the reader
- than ``trmgStat``; avoid contractions.
+ variable. Avoid contractions: ``transmogrifierStatus`` is more useful
+ to the reader than ``trmgStat``.
* A deferred that will be immediately returned may be called ``d``:
@@ -271,9 +258,7 @@
d.addErrback(onError)
return d
- * We prefer ``inlineCallbacks`` over ``deferredGenerator``.
- ``inlineCallbacks`` are more readable, and we do not support Python
- versions old enough that ``deferredGenerator`` would be necessary.
+ * Do not use ``deferredGenerator``. Use ``inlineCallbacks`` instead.
* That said, avoid using ``inlineCallbacks`` when chaining deferreds
is straightforward, as they are more expensive. Use
@@ -306,17 +291,29 @@
Use of underscores is reserved for implied dispatching and the like
(eg. ``http_FOO()``). See the Twisted Coding Standard for details.
- * Always use a tuple when using ``%``-formatting, even when only one
- value is being provided:
+ * Do not use ``%``-formatting:
::
error = "Unexpected value: %s" % (value,)
- Do not use the non-tuple form:
+ Use PEP-3101 formatting instead:
::
+ error = "Unexpected value: {value}".format(value=value)
+
+ * If you must use ``%``-formatting for some reason, always use a tuple as
+ the format argument, even when only one value is being provided:
+
+ ::
+
+ error = "Unexpected value: %s" % (value,)
+
+ Never use the non-tuple form:
+
+ ::
+
error = "Unexpected value: %s" % value
Which is allowed in Python, but results in a programming error if
@@ -329,8 +326,9 @@
numbers = (1,2,3,) # No
numbers = (1,2,3) # Yes
- It's desirable on multiple lines, though, as that makes re-ordering
- items easy, and avoids a diff on the last line when adding another:
+ The trailing comma is desirable on multiple lines, though, as that makes
+ re-ordering items easy, and avoids a diff on the last line when adding
+ another:
::
@@ -368,11 +366,11 @@
==============
* If a callable is going to return a Deferred some of the time, it
- should probably return a deferred all of the time. Return
- ``succeed(value)`` instead of ``value`` if necessary. This avoids
- forcing the caller to check as to whether the value is a deferred
- or not (eg. by using ``maybeDeferred()``), which is both annoying
- to code and potentially expensive at runtime.
+ should return a deferred all of the time. Return ``succeed(value)``
+ instead of ``value`` if necessary. This avoids forcing the caller
+ to check as to whether the value is a deferred or not (eg. by using
+ ``maybeDeferred()``), which is both annoying to code and potentially
+ expensive at runtime.
* Be proactive about closing files and file-like objects.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -60,11 +60,11 @@
noConfigOption = [
"calendarserver_bootstrap_database",
"calendarserver_load_augmentdb",
- "calendarserver_make_partition",
"calendarserver_manage_augments",
"calendarserver_manage_postgres",
"calendarserver_manage_timezones",
"icalendar_split",
+ "twistd", "trial",
]
if split(sys.argv[0])[-1] not in noConfigOption:
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-##
-# Copyright (c) 2010-2013 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 sys
-
-#PYTHONPATH
-
-if __name__ == "__main__":
- if "PYTHONPATH" in globals():
- sys.path.insert(0, PYTHONPATH)
- else:
- try:
- import _calendarserver_preamble
- except ImportError:
- sys.exc_clear()
-
- from calendarserver.tools.makepartition import main
- main()
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial (from rev 12016, CalendarServer/trunk/bin/trial)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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 sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+ sys.path.insert(0, PYTHONPATH)
+ else:
+ try:
+ import _calendarserver_preamble
+ except ImportError:
+ sys.exc_clear()
+
+ from twisted.scripts.trial import run
+ run()
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd (from rev 12016, CalendarServer/trunk/bin/twistd)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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 sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+ sys.path.insert(0, PYTHONPATH)
+ else:
+ try:
+ import _calendarserver_preamble
+ except ImportError:
+ sys.exc_clear()
+
+ from twisted.scripts.twistd import run
+ run()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -23,7 +23,9 @@
import time
import uuid
+from calendarserver.push.util import PushPriority
+
log = Logger()
@@ -49,7 +51,8 @@
class NotificationForID(amp.Command):
arguments = [('id', amp.String()),
- ('dataChangedTimestamp', amp.Integer(optional=True))]
+ ('dataChangedTimestamp', amp.Integer(optional=True)),
+ ('priority', amp.Integer(optional=True))]
response = [('status', amp.String())]
@@ -82,12 +85,14 @@
@inlineCallbacks
- def enqueue(self, transaction, id, dataChangedTimestamp=None):
+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=PushPriority.high):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
for protocol in self.protocols:
yield protocol.callRemote(NotificationForID, id=id,
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
@@ -103,10 +108,12 @@
@NotificationForID.responder
- def enqueueFromWorker(self, id, dataChangedTimestamp=None):
+ def enqueueFromWorker(self, id, dataChangedTimestamp=None,
+ priority=PushPriority.high.value):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
- self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp)
+ self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.lookupByValue(priority))
return {"status" : "OK"}
@@ -167,7 +174,8 @@
self.subscribers.remove(p)
- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
"""
Sends an AMP push notification to any clients subscribing to this pushKey.
@@ -192,23 +200,26 @@
if token is not None:
tokens.append(token)
if tokens:
- return self.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
+ return self.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
@inlineCallbacks
- def sendNotification(self, token, id, dataChangedTimestamp):
+ def sendNotification(self, token, id, dataChangedTimestamp, priority):
for subscriber in self.subscribers:
if subscriber.subscribedToID(id):
- yield subscriber.notify(token, id, dataChangedTimestamp)
+ yield subscriber.notify(token, id, dataChangedTimestamp,
+ priority)
@inlineCallbacks
- def scheduleNotifications(self, tokens, id, dataChangedTimestamp):
+ def scheduleNotifications(self, tokens, id, dataChangedTimestamp, priority):
if self.scheduler is not None:
- self.scheduler.schedule(tokens, id, dataChangedTimestamp)
+ self.scheduler.schedule(tokens, id, dataChangedTimestamp, priority)
else:
for token in tokens:
- yield self.sendNotification(token, id, dataChangedTimestamp)
+ yield self.sendNotification(token, id, dataChangedTimestamp,
+ priority)
@@ -238,11 +249,12 @@
return {"status" : "OK"}
UnsubscribeFromID.responder(unsubscribe)
- def notify(self, token, id, dataChangedTimestamp):
+ def notify(self, token, id, dataChangedTimestamp, priority):
if self.subscribedToID(id) == token:
self.log.debug("Sending notification for %s to %s" % (id, token))
return self.callRemote(NotificationForID, id=id,
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
def subscribedToID(self, id):
@@ -288,8 +300,8 @@
@inlineCallbacks
- def notificationForID(self, id, dataChangedTimestamp):
- yield self.callback(id, dataChangedTimestamp)
+ def notificationForID(self, id, dataChangedTimestamp, priority):
+ yield self.callback(id, dataChangedTimestamp, PushPriority.lookupByValue(priority))
returnValue({"status" : "OK"})
NotificationForID.responder(notificationForID)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -36,16 +36,27 @@
import struct
import time
from txdav.common.icommondatastore import InvalidSubscriptionValues
-
-from calendarserver.push.util import validToken, TokenHistory, PushScheduler
-
+from calendarserver.push.util import (
+ validToken, TokenHistory, PushScheduler, PushPriority
+)
from twext.internet.adaptendpoint import connect
from twext.internet.gaiendpoint import GAIEndpoint
+from twisted.python.constants import Values, ValueConstant
log = Logger()
+class ApplePushPriority(Values):
+ """
+ Maps calendarserver.push.util.PushPriority values to APNS-specific values
+ """
+ low = ValueConstant(PushPriority.low.value)
+ medium = ValueConstant(PushPriority.medium.value)
+ high = ValueConstant(PushPriority.high.value)
+
+
+
class ApplePushNotifierService(service.MultiService):
"""
ApplePushNotifierService is a MultiService responsible for
@@ -55,7 +66,7 @@
The Apple Push Notification protocol is described here:
- http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html
+ https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html
"""
log = Logger()
@@ -177,7 +188,8 @@
@inlineCallbacks
- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
"""
Sends an Apple Push Notification to any device token subscribed to
this pushKey.
@@ -191,6 +203,8 @@
@param dataChangedTimestamp: Timestamp (epoch seconds) for the data change
which triggered this notification (Only used for unit tests)
@type key: C{int}
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
try:
@@ -219,7 +233,8 @@
if token and uid:
tokens.append(token)
if tokens:
- provider.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
+ provider.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
@@ -230,8 +245,7 @@
log = Logger()
# Sent by provider
- COMMAND_SIMPLE = 0
- COMMAND_ENHANCED = 1
+ COMMAND_PROVIDER = 2
# Received by provider
COMMAND_ERROR = 8
@@ -333,7 +347,7 @@
yield txn.commit()
- def sendNotification(self, token, key, dataChangedTimestamp):
+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
"""
Sends a push notification message for the key to the device associated
with the token.
@@ -357,6 +371,7 @@
return
identifier = self.history.add(token)
+ apnsPriority = ApplePushPriority.lookupByValue(priority.value).value
payload = json.dumps(
{
"key" : key,
@@ -365,23 +380,79 @@
}
)
payloadLength = len(payload)
- self.log.debug("Sending APNS notification to {token}: id={id} payload={payload}",
- token=token, id=identifier, payload=payload)
+ self.log.debug("Sending APNS notification to {token}: id={id} payload={payload} priority={priority}",
+ token=token, id=identifier, payload=payload, priority=apnsPriority)
+ """
+ Notification format
+
+ Top level: Command (1 byte), Frame length (4 bytes), Frame data (variable)
+ Within Frame data: Item ...
+ Item: Item number (1 byte), Item data length (2 bytes), Item data (variable)
+ Item 1: Device token (32 bytes)
+ Item 2: Payload (variable length) in JSON format, not null-terminated
+ Item 3: Notification ID (4 bytes) an opaque value used for reporting errors
+ Item 4: Expiration date (4 bytes) UNIX epoch in secondcs UTC
+ Item 5: Priority (1 byte): 10 (push sent immediately) or 5 (push sent
+ at a time that conservces power on the device receiving it)
+ """
+
+ # Frame struct.pack format
+ # ! Network byte order
+ command = self.COMMAND_PROVIDER # B
+ frameLength = ( # I
+ # Item 1 (Device token)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 32 + # device token # 32s
+ # Item 2 (Payload)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ payloadLength + # the JSON payload # %d s
+ # Item 3 (Notification ID)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Notification ID # I
+ # Item 4 (Expiration)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Expiration seconds since epoch # I
+ # Item 5 (Priority)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 1 # Priority # B
+ )
+
self.transport.write(
- struct.pack("!BIIH32sH%ds" % (payloadLength,),
- self.COMMAND_ENHANCED, # Command
- identifier, # Identifier
- int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
+ struct.pack("!BIBH32sBH%dsBHIBHIBHB" % (payloadLength,),
+
+ command, # Command
+ frameLength, # Frame length
+
+ 1, # Item 1 (Device token)
32, # Token Length
binaryToken, # Token
- payloadLength, # Payload Length
- payload, # Payload in JSON format
+
+ 2, # Item 2 (Payload)
+ payloadLength, # Payload length
+ payload, # Payload
+
+ 3, # Item 3 (Notification ID)
+ 4, # Notification ID Length
+ identifier, # Notification ID
+
+ 4, # Item 4 (Expiration)
+ 4, # Expiration length
+ int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
+
+ 5, # Item 5 (Priority)
+ 1, # Priority length
+ apnsPriority, # Priority
+
)
)
-
class APNProviderFactory(ReconnectingClientFactory):
log = Logger()
@@ -509,12 +580,13 @@
# sent will be put back into the queue.
queued = list(self.queue)
self.queue = []
- for (token, key), dataChangedTimestamp in queued:
- if token and key and dataChangedTimestamp:
- self.sendNotification(token, key, dataChangedTimestamp)
+ for (token, key), dataChangedTimestamp, priority in queued:
+ if token and key and dataChangedTimestamp and priority:
+ self.sendNotification(token, key, dataChangedTimestamp,
+ priority)
- def scheduleNotifications(self, tokens, key, dataChangedTimestamp):
+ def scheduleNotifications(self, tokens, key, dataChangedTimestamp, priority):
"""
The starting point for getting notifications to the APNS server. If there is
a connection to the APNS server, these notifications are scheduled (or directly
@@ -533,15 +605,15 @@
connection = getattr(self.factory, "connection", None)
if connection is not None:
if self.scheduler is not None:
- self.scheduler.schedule(tokens, key, dataChangedTimestamp)
+ self.scheduler.schedule(tokens, key, dataChangedTimestamp, priority)
else:
for token in tokens:
- self.sendNotification(token, key, dataChangedTimestamp)
+ self.sendNotification(token, key, dataChangedTimestamp, priority)
else:
- self._saveForWhenConnected(tokens, key, dataChangedTimestamp)
+ self._saveForWhenConnected(tokens, key, dataChangedTimestamp, priority)
- def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp):
+ def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp, priority):
"""
Called in order to save notifications that can't be sent now because there
is no connection to the APNS server. (token, key) tuples are appended to
@@ -557,16 +629,16 @@
"""
for token in tokens:
tokenKeyPair = (token, key)
- for existingPair, ignored in self.queue:
+ for existingPair, timstamp, priority in self.queue:
if tokenKeyPair == existingPair:
self.log.debug("APNProviderService has no connection; skipping duplicate: %s %s" % (token, key))
break # Already scheduled
else:
self.log.debug("APNProviderService has no connection; queuing: %s %s" % (token, key))
- self.queue.append(((token, key), dataChangedTimestamp))
+ self.queue.append(((token, key), dataChangedTimestamp, priority))
- def sendNotification(self, token, key, dataChangedTimestamp):
+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
"""
If there is a connection the notification is sent right away, otherwise
the notification is saved for later.
@@ -579,15 +651,15 @@
which triggered this notification
@type key: C{int}
"""
- if not (token and key and dataChangedTimestamp):
+ if not (token and key and dataChangedTimestamp, priority):
return
# Service has reference to factory has reference to protocol instance
connection = getattr(self.factory, "connection", None)
if connection is None:
- self._saveForWhenConnected([token], key, dataChangedTimestamp)
+ self._saveForWhenConnected([token], key, dataChangedTimestamp, priority)
else:
- connection.sendNotification(token, key, dataChangedTimestamp)
+ connection.sendNotification(token, key, dataChangedTimestamp, priority)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,7 +19,7 @@
"""
from twext.enterprise.dal.record import fromTable
-from twext.enterprise.dal.syntax import Delete
+from twext.enterprise.dal.syntax import Delete, Select, Parameter
from twext.enterprise.queue import WorkItem
from twext.python.log import Logger
@@ -32,10 +32,13 @@
import datetime
+from calendarserver.push.util import PushPriority
log = Logger()
+
+
class PushNotificationWork(WorkItem, fromTable(schema.PUSH_NOTIFICATION_WORK)):
group = property(lambda self: self.pushID)
@@ -43,14 +46,35 @@
@inlineCallbacks
def doWork(self):
- # Delete all other work items with the same pushID
- yield Delete(From=self.table,
- Where=self.table.PUSH_ID == self.pushID
- ).on(self.transaction)
+ # Find all work items with the same push ID and find the highest
+ # priority. Delete matching work items.
+ results = (yield Select([self.table.WORK_ID, self.table.PRIORITY],
+ From=self.table, Where=self.table.PUSH_ID == self.pushID).on(
+ self.transaction))
+ maxPriority = self.priority
+
+ # If there are other enqueued work items for this push ID, find the
+ # highest priority one and use that value
+ if results:
+ workIDs = []
+ for workID, priority in results:
+ if priority > maxPriority:
+ maxPriority = priority
+ workIDs.append(workID)
+
+ # Delete the work items we selected
+ yield Delete(From=self.table,
+ Where=self.table.WORK_ID.In(
+ Parameter("workIDs", len(workIDs)))
+ ).on(self.transaction, workIDs=workIDs)
+
pushDistributor = self.transaction._pushDistributor
if pushDistributor is not None:
- yield pushDistributor.enqueue(self.transaction, self.pushID)
+ # Convert the integer priority value back into a constant
+ priority = PushPriority.lookupByValue(maxPriority)
+ yield pushDistributor.enqueue(self.transaction, self.pushID,
+ priority=priority)
@@ -84,13 +108,15 @@
@inlineCallbacks
- def notify(self, txn):
+ def notify(self, txn, priority=PushPriority.high):
"""
Send the notification. For a home object we just push using the home id. For a home
child we push both the owner home id and the owned home child id.
@param txn: The transaction to create the work item with
@type txn: L{CommonStoreTransaction}
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
# Push ids from the store objects are a tuple of (prefix, name,) and we need to compose that
# into a single token.
@@ -102,10 +128,13 @@
for prefix, id in ids:
if self._notify:
- self.log.debug("Notifications are enabled: %s %s/%s" % (self._storeObject, prefix, id,))
- yield self._notifierFactory.send(prefix, id, txn)
+ self.log.debug("Notifications are enabled: %s %s/%s priority=%d" %
+ (self._storeObject, prefix, id, priority.value))
+ yield self._notifierFactory.send(prefix, id, txn,
+ priority=priority)
else:
- self.log.debug("Skipping notification for: %s %s/%s" % (self._storeObject, prefix, id,))
+ self.log.debug("Skipping notification for: %s %s/%s" %
+ (self._storeObject, prefix, id,))
def clone(self, storeObject):
@@ -150,12 +179,14 @@
@inlineCallbacks
- def send(self, prefix, id, txn):
+ def send(self, prefix, id, txn, priority=PushPriority.high):
"""
Enqueue a push notification work item on the provided transaction.
"""
notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=self.coalesceSeconds)
- yield txn.enqueue(PushNotificationWork, pushID=self.pushKeyForId(prefix, id), notBefore=notBefore)
+ yield txn.enqueue(PushNotificationWork,
+ pushID=self.pushKeyForId(prefix, id), notBefore=notBefore,
+ priority=priority.value)
def newNotifier(self, storeObject):
@@ -212,7 +243,7 @@
@inlineCallbacks
- def enqueue(self, transaction, pushKey):
+ def enqueue(self, transaction, pushKey, priority=PushPriority.high):
"""
Pass along enqueued pushKey to any observers
@@ -221,6 +252,10 @@
@param pushKey: the push key to distribute to the observers
@type pushKey: C{str}
+
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
for observer in self.observers:
- yield observer.enqueue(transaction, pushKey)
+ yield observer.enqueue(transaction, pushKey,
+ dataChangedTimestamp=None, priority=priority)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,6 +18,7 @@
from calendarserver.push.amppush import NotificationForID
from twistedcaldav.test.util import StoreTestCase
from twisted.internet.task import Clock
+from calendarserver.push.util import PushPriority
class AMPPushMasterTests(StoreTestCase):
@@ -57,27 +58,81 @@
self.assertTrue(client3.subscribedToID("/CalDAV/localhost/user03/"))
dataChangedTimestamp = 1354815999
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.high)
self.assertEquals(len(client1.history), 0)
self.assertEquals(len(client2.history), 0)
self.assertEquals(len(client3.history), 0)
clock.advance(1)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
self.assertEquals(len(client2.history), 0)
self.assertEquals(len(client3.history), 0)
clock.advance(3)
- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
+
self.assertEquals(len(client3.history), 0)
clock.advance(3)
- self.assertEquals(client3.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client3.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
client1.reset()
client2.reset()
client2.unsubscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.low)
self.assertEquals(len(client1.history), 0)
clock.advance(1)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.low.value,
+ }
+ )
+ ]
+ )
+
self.assertEquals(len(client2.history), 0)
clock.advance(3)
self.assertEquals(len(client2.history), 0)
@@ -87,9 +142,35 @@
client1.reset()
client2.reset()
client2.subscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.medium)
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,14 +18,15 @@
import struct
import time
from calendarserver.push.applepush import (
- ApplePushNotifierService, APNProviderProtocol
+ ApplePushNotifierService, APNProviderProtocol, ApplePushPriority
)
-from calendarserver.push.util import validToken, TokenHistory
+from calendarserver.push.util import validToken, TokenHistory, PushPriority
from twistedcaldav.test.util import StoreTestCase
from twisted.internet.defer import inlineCallbacks, succeed
from twisted.internet.task import Clock
from txdav.common.icommondatastore import InvalidSubscriptionValues
+
class ApplePushNotifierServiceTests(StoreTestCase):
@inlineCallbacks
@@ -120,12 +121,14 @@
dataChangedTimestamp = 1354815999
txn = self._sqlCalendarStore.newTransaction()
yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp, priority=PushPriority.high)
yield txn.commit()
# The notifications should be in the queue
- self.assertTrue(((token, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
- self.assertTrue(((token2, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
+ self.assertTrue(((token, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
+ self.assertTrue(((token2, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
# Start the service, making the connection which should service the
# queue
@@ -137,17 +140,40 @@
# Verify data sent to APN
providerConnector = service.providers["CalDAV"].testConnector
rawData = providerConnector.transport.data
- self.assertEquals(len(rawData), 183)
- data = struct.unpack("!BIIH32sH", rawData[:45])
- self.assertEquals(data[0], 1) # command
- self.assertEquals(data[4].encode("hex"), token.replace(" ", "")) # token
- payloadLength = data[5]
- payload = struct.unpack("%ds" % (payloadLength,),
- rawData[45:])
+ self.assertEquals(len(rawData), 199)
+ data = struct.unpack("!BI", rawData[:5])
+ self.assertEquals(data[0], 2) # command
+ self.assertEquals(data[1], 194) # frame length
+ # Item 1 (device token)
+ data = struct.unpack("!BH32s", rawData[5:40])
+ self.assertEquals(data[0], 1)
+ self.assertEquals(data[1], 32)
+ self.assertEquals(data[2].encode("hex"), token.replace(" ", "")) # token
+ # Item 2 (payload)
+ data = struct.unpack("!BH", rawData[40:43])
+ self.assertEquals(data[0], 2)
+ payloadLength = data[1]
+ self.assertEquals(payloadLength, 138)
+ payload = struct.unpack("!%ds" % (payloadLength,), rawData[43:181])
payload = json.loads(payload[0])
self.assertEquals(payload["key"], u"/CalDAV/calendars.example.com/user01/calendar/")
self.assertEquals(payload["dataChangedTimestamp"], dataChangedTimestamp)
self.assertTrue("pushRequestSubmittedTimestamp" in payload)
+ # Item 3 (notification id)
+ data = struct.unpack("!BHI", rawData[181:188])
+ self.assertEquals(data[0], 3)
+ self.assertEquals(data[1], 4)
+ self.assertEquals(data[2], 2)
+ # Item 4 (expiration)
+ data = struct.unpack("!BHI", rawData[188:195])
+ self.assertEquals(data[0], 4)
+ self.assertEquals(data[1], 4)
+ # Item 5 (priority)
+ data = struct.unpack("!BHB", rawData[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.high.value)
+
# Verify token history is updated
self.assertTrue(token in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
self.assertTrue(token2 in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
@@ -160,14 +186,21 @@
providerConnector.transport.data = None
# Send notification while service is connected
txn = self._sqlCalendarStore.newTransaction()
- yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/")
+ yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
+ priority=PushPriority.low)
yield txn.commit()
clock.advance(1) # so that first push is sent
- self.assertEquals(len(providerConnector.transport.data), 183)
+ self.assertEquals(len(providerConnector.transport.data), 199)
+ # Ensure that the priority is "low"
+ data = struct.unpack("!BHB", providerConnector.transport.data[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.low.value)
+
# Reset sent data
providerConnector.transport.data = None
clock.advance(3) # so that second push is sent
- self.assertEquals(len(providerConnector.transport.data), 183)
+ self.assertEquals(len(providerConnector.transport.data), 199)
history = []
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,12 +18,13 @@
from calendarserver.push.notifier import PushDistributor
from calendarserver.push.notifier import getPubSubAPSConfiguration
from calendarserver.push.notifier import PushNotificationWork
-from twisted.internet.defer import inlineCallbacks, succeed
+from twisted.internet.defer import inlineCallbacks, succeed, gatherResults
from twistedcaldav.config import ConfigDict
from txdav.common.datastore.test.util import populateCalendarsFrom
from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
+from calendarserver.push.util import PushPriority
+from txdav.idav import ChangeCategory
-
class StubService(object):
def __init__(self):
self.reset()
@@ -33,8 +34,9 @@
self.history = []
- def enqueue(self, transaction, id):
- self.history.append(id)
+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((id, priority))
return(succeed(None))
@@ -45,8 +47,8 @@
def test_enqueue(self):
stub = StubService()
dist = PushDistributor([stub])
- yield dist.enqueue(None, "testing")
- self.assertEquals(stub.history, ["testing"])
+ yield dist.enqueue(None, "testing", PushPriority.high)
+ self.assertEquals(stub.history, [("testing", PushPriority.high)])
def test_getPubSubAPSConfiguration(self):
@@ -91,11 +93,12 @@
self.history = []
- def enqueue(self, transaction, pushID):
- self.history.append(pushID)
+ def enqueue(self, transaction, pushID, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((pushID, priority))
+ return(succeed(None))
-
class PushNotificationWorkTests(StoreTestCase):
@inlineCallbacks
@@ -111,35 +114,71 @@
txn = self._sqlCalendarStore.newTransaction()
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/foo/",
+ priority=PushPriority.high.value
))
yield txn.commit()
yield wp.whenExecuted()
- self.assertEquals(pushDistributor.history, ["/CalDAV/localhost/foo/"])
+ self.assertEquals(pushDistributor.history,
+ [("/CalDAV/localhost/foo/", PushPriority.high)])
pushDistributor.reset()
txn = self._sqlCalendarStore.newTransaction()
- wp = (yield txn.enqueue(PushNotificationWork,
+ proposals = []
+ wp = txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
- ))
- wp = (yield txn.enqueue(PushNotificationWork,
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
- ))
- wp = (yield txn.enqueue(PushNotificationWork,
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
- ))
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
# Enqueue a different pushID to ensure those are not grouped with
# the others:
- wp = (yield txn.enqueue(PushNotificationWork,
+ wp = txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/baz/",
- ))
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
yield txn.commit()
- yield wp.whenExecuted()
+ yield gatherResults(proposals)
+ self.assertEquals(set(pushDistributor.history),
+ set([("/CalDAV/localhost/bar/", PushPriority.high),
+ ("/CalDAV/localhost/baz/", PushPriority.high)]))
+
+ # Ensure only the high-water-mark priority push goes out, by
+ # enqueuing low, medium, and high notifications
+ pushDistributor.reset()
+ txn = self._sqlCalendarStore.newTransaction()
+ proposals = []
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.low.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.medium.value
+ )
+ proposals.append(wp.whenExecuted())
+ yield txn.commit()
+ yield gatherResults(proposals)
self.assertEquals(pushDistributor.history,
- ["/CalDAV/localhost/bar/", "/CalDAV/localhost/baz/"])
+ [("/CalDAV/localhost/bar/", PushPriority.high)])
-
class NotifierFactory(StoreTestCase):
requirements = {
@@ -168,8 +207,9 @@
def test_homeNotifier(self):
home = yield self.homeUnderTest()
- yield home.notifyChanged()
- self.assertEquals(self.notifierFactory.history, ["/CalDAV/example.com/home1/"])
+ yield home.notifyChanged(category=ChangeCategory.default)
+ self.assertEquals(self.notifierFactory.history,
+ [("/CalDAV/example.com/home1/", PushPriority.high)])
yield self.commit()
@@ -177,10 +217,12 @@
def test_calendarNotifier(self):
calendar = yield self.calendarUnderTest()
- yield calendar.notifyChanged()
+ yield calendar.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
)
yield self.commit()
@@ -194,9 +236,9 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
])
)
yield self.commit()
@@ -207,9 +249,9 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
])
)
yield self.commit()
@@ -225,10 +267,12 @@
self.notifierFactory.reset()
shared = yield self.calendarUnderTest(home="home2", name=shareName)
- yield shared.notifyChanged()
+ yield shared.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
)
yield self.commit()
@@ -237,9 +281,11 @@
def test_notificationNotifier(self):
notifications = yield self.transactionUnderTest().notificationsWithUID("home1")
- yield notifications.notifyChanged()
+ yield notifications.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/notification/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high)])
)
yield self.commit()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,20 @@
from OpenSSL import crypto
from twext.python.log import Logger
+from twisted.python.constants import Values, ValueConstant
+
+
+class PushPriority(Values):
+ """
+ Constants to use for push priorities
+ """
+ low = ValueConstant(1)
+ medium = ValueConstant(5)
+ high = ValueConstant(10)
+
+
+
def getAPNTopicFromCertificate(certPath):
"""
Given the path to a certificate, extract the UID value portion of the
@@ -128,7 +141,7 @@
self.staggerSeconds = staggerSeconds
- def schedule(self, tokens, key, dataChangedTimestamp):
+ def schedule(self, tokens, key, dataChangedTimestamp, priority):
"""
Schedules a batch of notifications for the given tokens, staggered
with self.staggerSeconds between each one. Duplicates are ignored,
@@ -151,13 +164,14 @@
(internalKey,))
else:
self.outstanding[internalKey] = self.reactor.callLater(
- scheduleTime, self.send, token, key, dataChangedTimestamp)
+ scheduleTime, self.send, token, key, dataChangedTimestamp,
+ priority)
self.log.debug("PushScheduler scheduled: %s in %.0f sec" %
(internalKey, scheduleTime))
scheduleTime += self.staggerSeconds
- def send(self, token, key, dataChangedTimestamp):
+ def send(self, token, key, dataChangedTimestamp, priority):
"""
This method is what actually gets scheduled. Its job is to remove
its corresponding entry from the outstanding dict and call the
@@ -173,7 +187,7 @@
"""
self.log.debug("PushScheduler fired for %s %s %d" % (token, key, dataChangedTimestamp))
del self.outstanding[(token, key)]
- return self.callback(token, key, dataChangedTimestamp)
+ return self.callback(token, key, dataChangedTimestamp, priority)
def stop(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -401,6 +401,14 @@
print("Reading configuration from file: %s" % (self["config"],))
config.load(self["config"])
+
+ for path in config.getProvider().importedFiles:
+ print("Imported configuration from file: '%s'" % (path,))
+ for path in config.getProvider().includedFiles:
+ print("Adding configuration from file: '%s'" % (path,))
+ for path in config.getProvider().missingFiles:
+ print("Missing configuration file: '%s'" % (path,))
+
config.updateDefaults(self.overrides)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -636,11 +636,23 @@
addSystemEventTrigger("after", "startup", timezoneStdService.onStartup)
#
- # iSchedule service
+ # iSchedule service for podding
#
+ if config.Servers.Enabled:
+ log.info("Setting up iSchedule podding inbox resource: {cls}", cls=iScheduleResourceClass)
+
+ ischedule = iScheduleResourceClass(
+ root,
+ newStore,
+ podding=True
+ )
+ root.putChild(config.Servers.InboxName, ischedule)
+
+ #
+ # iSchedule service (not used for podding)
+ #
if config.Scheduling.iSchedule.Enabled:
- log.info("Setting up iSchedule inbox resource: {cls}",
- cls=iScheduleResourceClass)
+ log.info("Setting up iSchedule inbox resource: {cls}", cls=iScheduleResourceClass)
ischedule = iScheduleResourceClass(
root,
@@ -651,8 +663,7 @@
# Do DomainKey resources
DKIMUtils.validConfiguration(config)
if config.Scheduling.iSchedule.DKIM.Enabled:
- log.info("Setting up domainkey resource: {res}",
- res=DomainKeyResource)
+ log.info("Setting up domainkey resource: {res}", res=DomainKeyResource)
domain = config.Scheduling.iSchedule.DKIM.Domain if config.Scheduling.iSchedule.DKIM.Domain else config.ServerHostName
dk = DomainKeyResource(
domain,
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -141,8 +141,8 @@
-def notificationCallback(id, dataChangedTimestamp):
- print("Received notification for:", id)
+def notificationCallback(id, dataChangedTimestamp, priority):
+ print("Received notification for:", id, "Priority", priority)
return succeed(True)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -33,8 +33,8 @@
from twext.python.plistlib import readPlistFromString
-from pycalendar.calendar import PyCalendar
-from pycalendar.attribute import PyCalendarAttribute
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.parameter import Parameter
COPY_CAL_XATTRS = (
'WebDAV:{DAV:}resourcetype',
@@ -67,6 +67,8 @@
else:
sys.exit(0)
+
+
def main():
try:
@@ -83,8 +85,6 @@
# Get configuration
#
directoryNode = "/Search"
- sourceDirectory = None
- destDirectory = None
for opt, arg in optargs:
if opt in ("-h", "--help"):
@@ -107,6 +107,7 @@
directoryMap.dumpDsImports(os.path.join(destDirectory, "dsimports"))
+
def anonymizeRoot(directoryMap, sourceDirectory, destDirectory):
# sourceDirectory and destDirectory are DocumentRoots
@@ -244,12 +245,10 @@
resources += 1
-
# Store new ctag on calendar
xml = "<?xml version='1.0' encoding='UTF-8'?>\r\n<getctag xmlns='http://calendarserver.org/ns/'>%s</getctag>\r\n" % (str(datetime.datetime.now()),)
xattr.setxattr(destCal, "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag", zlib.compress(xml))
-
# Calendar home quota
xml = "<?xml version='1.0' encoding='UTF-8'?>\r\n<quota-used xmlns='http://twistedmatrix.com/xml_namespace/dav/private/'>%d</quota-used>\r\n" % (quotaUsed,)
xattr.setxattr(destHome, "WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2Fprivate%2F}quota-used", zlib.compress(xml))
@@ -280,9 +279,10 @@
print("")
+
def anonymizeData(directoryMap, data):
try:
- pyobj = PyCalendar.parseText(data)
+ pyobj = Calendar.parseText(data)
except Exception, e:
print("Failed to parse (%s): %s" % (e, data))
return None
@@ -309,13 +309,13 @@
comp.removeProperty(prop)
continue
prop.setValue("urn:uuid:%s" % (record['guid'],))
- if prop.hasAttribute('X-CALENDARSERVER-EMAIL'):
- prop.replaceAttribute(PyCalendarAttribute('X-CALENDARSERVER-EMAIL', record['email']))
+ if prop.hasParameter('X-CALENDARSERVER-EMAIL'):
+ prop.replaceParameter(Parameter('X-CALENDARSERVER-EMAIL', record['email']))
else:
- prop.removeAttributes('EMAIL')
- prop.addAttribute(PyCalendarAttribute('EMAIL', record['email']))
- prop.removeAttributes('CN')
- prop.addAttribute(PyCalendarAttribute('CN', record['name']))
+ prop.removeParameters('EMAIL')
+ prop.addParameter(Parameter('EMAIL', record['email']))
+ prop.removeParameters('CN')
+ prop.addParameter(Parameter('CN', record['name']))
except KeyError:
pass
@@ -345,11 +345,12 @@
return pyobj.getText(includeTimezones=True)
+
class DirectoryMap(object):
def __init__(self, node):
- self.map = { }
+ self.map = {}
self.byType = {
'users' : [],
'groups' : [],
@@ -373,10 +374,10 @@
print("Fetching records from directory: %s" % (node,))
- for internalType, (recordType, friendlyType) in self.strings.iteritems():
+ for internalType, (recordType, _ignore_friendlyType) in self.strings.iteritems():
print(" %s..." % (internalType,))
child = Popen(
- args = [
+ args=[
"/usr/bin/dscl", "-plist", node, "-readall",
"/%s" % (recordType,),
"GeneratedUID", "RecordName", "EMailAddress", "GroupMembers"
@@ -405,6 +406,7 @@
print("Done.")
print("")
+
def addRecord(self, internalType="users", guid=None, names=None,
emails=None, members=None, cua=None):
@@ -458,6 +460,7 @@
keys.append(email)
return keys
+
def cua2key(self, cua):
key = cua.lower()
@@ -473,10 +476,11 @@
return key
+
def lookupCUA(self, cua):
key = self.cua2key(cua)
- if key and self.map.has_key(key):
+ if key and key in self.map:
return self.map[key]
else:
return None
@@ -491,6 +495,7 @@
if unknown:
print(" Principals not found in directory: %d" % (unknown,))
+
def dumpDsImports(self, dirPath):
if not os.path.exists(dirPath):
os.makedirs(dirPath)
@@ -567,6 +572,8 @@
Error trying to access dscl
"""
+
+
class DatabaseError(Exception):
"""
Error trying to access sqlite3
@@ -574,8 +581,6 @@
-
-
def anonymize(text):
"""
Return a string whose value is the hex digest of text, repeated as needed
@@ -592,19 +597,18 @@
h = hashlib.md5(text)
h = h.hexdigest()
l = len(text)
- return (h*((l/32)+1))[:-(32-(l%32))]
+ return (h * ((l / 32) + 1))[:-(32 - (l % 32))]
nameChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def randomName(length):
l = []
- for i in xrange(length):
+ for _ignore in xrange(length):
l.append(random.choice(nameChars))
return "".join(l)
-
if __name__ == "__main__":
main()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -47,12 +47,12 @@
import traceback
import uuid
-from pycalendar import definitions
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarError
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import ErrorBase
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
from twisted.application.service import Service
from twisted.internet.defer import inlineCallbacks, returnValue
@@ -543,7 +543,7 @@
tr = schema.TIME_RANGE
kwds = {
"Start" : pyCalendarTodatetime(start),
- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0))
+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0))
}
rows = (yield Select(
[ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED],
@@ -596,7 +596,7 @@
tr = schema.TIME_RANGE
kwds = {
"Start" : pyCalendarTodatetime(start),
- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0)),
"UUID" : uuid,
}
rows = (yield Select(
@@ -626,7 +626,7 @@
kwds = {
"Start" : pyCalendarTodatetime(start),
- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0)),
"UUID" : uuid,
}
rows = (yield Select(
@@ -704,8 +704,8 @@
),
).on(self.txn, **kwds))
try:
- caldata = PyCalendar.parseText(rows[0][0]) if rows else None
- except PyCalendarError:
+ caldata = Calendar.parseText(rows[0][0]) if rows else None
+ except ErrorBase:
caltxt = rows[0][0] if rows else None
if caltxt:
caltxt = caltxt.replace("\r\n ", "")
@@ -713,8 +713,8 @@
if doFix:
caltxt = (yield self.fixBadOldCua(resid, caltxt))
try:
- caldata = PyCalendar.parseText(caltxt) if rows else None
- except PyCalendarError:
+ caldata = Calendar.parseText(caltxt) if rows else None
+ except ErrorBase:
self.parseError = "No fix bad CALENDARSERVER-OLD-CUA"
returnValue(None)
else:
@@ -746,8 +746,8 @@
).on(self.txn, **kwds))
try:
- caldata = PyCalendar.parseText(rows[0][0]) if rows else None
- except PyCalendarError:
+ caldata = Calendar.parseText(rows[0][0]) if rows else None
+ except ErrorBase:
returnValue((None, None, None, None,))
returnValue((caldata, rows[0][1], rows[0][2], rows[0][3],) if rows else (None, None, None, None,))
@@ -1056,14 +1056,14 @@
self.output.write("\n---- Scanning calendar data ----\n")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = PyCalendarDateTime.getToday()
+ self.now = DateTime.getNowUTC()
+ self.start = DateTime.getToday()
self.start.setDateOnly(False)
self.end = self.start.duplicate()
self.end.offsetYear(1)
self.fix = self.options["fix"]
- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
self.txn = self.store.newTransaction()
@@ -1426,14 +1426,14 @@
self.output.write("\n---- Scanning calendar data ----\n")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
+ self.now = DateTime.getNowUTC()
+ self.start = self.options["start"] if "start" in self.options else DateTime.getToday()
self.start.setDateOnly(False)
self.end = self.start.duplicate()
self.end.offsetYear(1)
self.fix = self.options["fix"]
- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
self.txn = self.store.newTransaction()
@@ -2028,7 +2028,7 @@
"""
Return the master iCal component in this calendar.
- @return: the L{PyCalendarComponent} for the master component,
+ @return: the L{Component} for the master component,
or C{None} if there isn't one.
"""
for component in calendar.getComponents(definitions.cICalComponent_VEVENT):
@@ -2042,7 +2042,7 @@
# Expand events into instances in the start/end range
results = []
calendar.getVEvents(
- PyCalendarPeriod(
+ Period(
start=start,
end=end,
),
@@ -2082,10 +2082,10 @@
if cancelled:
partstat = "CANCELLED"
else:
- if not prop.hasAttribute(definitions.cICalAttribute_PARTSTAT):
- partstat = definitions.cICalAttribute_PARTSTAT_NEEDSACTION
+ if not prop.hasParameter(definitions.cICalParameter_PARTSTAT):
+ partstat = definitions.cICalParameter_PARTSTAT_NEEDSACTION
else:
- partstat = prop.getAttributeValue(definitions.cICalAttribute_PARTSTAT)
+ partstat = prop.getParameterValue(definitions.cICalParameter_PARTSTAT)
attendees.setdefault(caladdr, set()).add((instance_id, partstat))
@@ -2140,9 +2140,9 @@
self.output.write("\n---- Scanning calendar data ----\n")
- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = PyCalendarDateTime.getToday()
+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.now = DateTime.getNowUTC()
+ self.start = DateTime.getToday()
self.start.setDateOnly(False)
self.start.setTimezone(self.tzid)
self.end = self.start.duplicate()
@@ -2328,7 +2328,7 @@
continue
dtstart = instance.component.propertyValue("DTSTART")
if tzid is None and dtstart.getTimezoneID():
- tzid = PyCalendarTimezone(tzid=dtstart.getTimezoneID())
+ tzid = Timezone(tzid=dtstart.getTimezoneID())
hasFloating |= dtstart.isDateOnly() or dtstart.floating()
details.append(InstanceDetails(resid, uid, instance.start, instance.end, instance.component.getOrganizer(), instance.component.propertyValue("SUMMARY")))
@@ -2369,7 +2369,7 @@
# Adjust floating and sort
if hasFloating and tzid is not None:
- utc = PyCalendarTimezone(utc=True)
+ utc = Timezone(utc=True)
for item in details:
if item.start.floating():
item.start.setTimezone(tzid)
@@ -2449,9 +2449,9 @@
self.output.write("\n---- Scanning calendar data ----\n")
- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.now = DateTime.getNowUTC()
+ self.start = self.options["start"] if "start" in self.options else DateTime.getToday()
self.start.setDateOnly(False)
self.start.setTimezone(self.tzid)
self.fix = self.options["fix"]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,7 +24,7 @@
from calendarserver.tools import tables
from calendarserver.tools.cmdline import utilityMain
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.enterprise.dal.syntax import Select, Parameter, Count, Delete, \
Constant
from twisted.application.service import Service
@@ -715,12 +715,12 @@
end += "T000000Z"
try:
- start = PyCalendarDateTime.parseText(start)
+ start = DateTime.parseText(start)
except ValueError:
print("Invalid start value")
returnValue(None)
try:
- end = PyCalendarDateTime.parseText(end)
+ end = DateTime.parseText(end)
except ValueError:
print("Invalid end value")
returnValue(None)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -36,9 +36,9 @@
from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
from calendarserver.tools.cmdline import utilityMain
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
-from twistedcaldav.config import config, ConfigDict
+from twistedcaldav.config import config, ConfigDict
from calendarserver.tools.config import WRITABLE_CONFIG_KEYS, setKeyPath, getKeyPath, flattenDictionary, WritableConfig
@@ -156,6 +156,7 @@
'ZIP' : { 'extras' : True, 'attr' : 'zip', },
'Country' : { 'extras' : True, 'attr' : 'country', },
'Phone' : { 'extras' : True, 'attr' : 'phone', },
+ 'Geo' : { 'extras' : True, 'attr' : 'geo', },
'AutoSchedule' : { 'attr' : 'autoSchedule', },
'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
}
@@ -373,7 +374,7 @@
return
self.respondWithRecordsOfTypes(self.dir, command, ["resources"])
-
+
def command_getLocationAndResourceList(self, command):
self.respondWithRecordsOfTypes(self.dir, command, ["locations", "resources"])
@@ -424,10 +425,8 @@
self.command_readConfig(command)
-
# Proxies
-
@inlineCallbacks
def command_listWriteProxies(self, command):
principal = principalForPrincipalID(command['Principal'], directory=self.dir)
@@ -538,14 +537,13 @@
@type command: C{dict}
"""
retainDays = command.get("RetainDays", DEFAULT_RETAIN_DAYS)
- cutoff = PyCalendarDateTime.getToday()
+ cutoff = DateTime.getToday()
cutoff.setDateOnly(False)
cutoff.offsetDay(-retainDays)
eventCount = (yield PurgeOldEventsService.purgeOldEvents(self.store, cutoff, DEFAULT_BATCH_SIZE))
self.respond(command, {'EventsRemoved' : eventCount, "RetainDays" : retainDays})
-
@inlineCallbacks
def respondWithProxies(self, directory, command, principal, proxyType):
proxies = []
@@ -562,7 +560,6 @@
})
-
def respondWithRecordsOfTypes(self, directory, command, recordTypes):
result = []
for recordType in recordTypes:
@@ -572,7 +569,6 @@
self.respond(command, result)
-
def respond(self, command, result):
self.output.write(writePlistToString({'command' : command['command'], 'result' : result}))
@@ -581,6 +577,7 @@
self.output.write(writePlistToString({'error' : msg, }))
+
def recordToDict(record):
recordDict = {}
for key, info in attrMap.iteritems():
@@ -596,6 +593,8 @@
pass
return recordDict
+
+
def respondWithError(msg, status=1):
sys.stdout.write(writePlistToString({'error' : msg, }))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,8 +16,8 @@
##
from __future__ import print_function
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
from twext.python.log import StandardIOObserver
@@ -95,11 +95,11 @@
pass
if not tzvers:
- tzvers = PyCalendarDateTime.getToday().getText()
+ tzvers = DateTime.getToday().getText()
print("Converting data (version: %s) at: %s" % (tzvers, zonedir,))
startYear = 1800
- endYear = PyCalendarDateTime.getToday().getYear() + 10
- PyCalendar.sProdID = "-//calendarserver.org//Zonal//EN"
+ endYear = DateTime.getToday().getYear() + 10
+ Calendar.sProdID = "-//calendarserver.org//Zonal//EN"
zonefiles = "northamerica", "southamerica", "europe", "africa", "asia", "australasia", "antarctica", "etcetera", "backward"
parser = tzconvert()
for file in zonefiles:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -23,7 +23,7 @@
from getopt import getopt, GetoptError
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.python.log import Logger
@@ -147,7 +147,7 @@
if dryrun:
verbose = True
- cutoff = PyCalendarDateTime.getToday()
+ cutoff = DateTime.getToday()
cutoff.setDateOnly(False)
cutoff.offsetDay(-days)
cls.cutoff = cutoff
@@ -328,7 +328,7 @@
cls.uuid = uuid
if days > 0:
- cutoff = PyCalendarDateTime.getToday()
+ cutoff = DateTime.getToday()
cutoff.setDateOnly(False)
cutoff.offsetDay(-days)
cls.cutoff = cutoff
@@ -352,7 +352,7 @@
service = cls(store)
service.uuid = uuid
if days > 0:
- cutoff = PyCalendarDateTime.getToday()
+ cutoff = DateTime.getToday()
cutoff.setDateOnly(False)
cutoff.offsetDay(-days)
service.cutoff = cutoff
@@ -732,7 +732,7 @@
def _purgeUID(self, uid):
if self.when is None:
- self.when = PyCalendarDateTime.getNowUTC()
+ self.when = DateTime.getNowUTC()
# Does the record exist?
record = self.directory.recordWithUID(uid)
@@ -1020,7 +1020,7 @@
@type event: L{twistedcaldav.ical.Component}
@param when: the cutoff date (anything after which is removed)
- @type when: PyCalendarDateTime
+ @type when: DateTime
@param cua: Calendar User Address of principal being purged, to compare
to see if it's the organizer of the event or just an attendee
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -41,6 +41,7 @@
returnValue(sorted(records, key=operator.attrgetter("fullName")))
+
@inlineCallbacks
def recordInfo(directory, record):
"""
@@ -58,8 +59,9 @@
add("Proxy access" , (yield recordProxyAccessInfo(directory, record)))
returnValue("\n".join(info))
-
+
+
def recordBasicInfo(directory, record):
"""
Basic information for a record.
@@ -87,15 +89,15 @@
for cua in record.calendarUserAddresses:
add("Calendar User Address", cua)
- add("Server ID" , record.serverID )
- add("Partition ID" , record.partitionID )
- add("Enabled" , record.enabled )
- add("Enabled for Calendar", record.enabledForCalendaring )
+ add("Server ID" , record.serverID)
+ add("Enabled" , record.enabled)
+ add("Enabled for Calendar", record.enabledForCalendaring)
add("Enabled for Contacts", record.enabledForAddressBooks)
return succeed(table.toString())
+
def recordGroupMembershipInfo(directory, record):
"""
Group membership info for a record.
@@ -109,7 +111,7 @@
return succeed(None)
rows = sorted(rows,
- key = lambda row: (row[1], row[2])
+ key=lambda row: (row[1], row[2])
)
table = Table()
@@ -120,6 +122,7 @@
return succeed(table.toString())
+
@inlineCallbacks
def recordProxyAccessInfo(directory, record):
"""
@@ -154,7 +157,7 @@
returnValue(None)
rows = sorted(rows,
- key = lambda row: (row[1], row[2], row[4])
+ key=lambda row: (row[1], row[2], row[4])
)
table = Table()
@@ -165,6 +168,7 @@
returnValue(table.toString())
+
def summarizeRecords(directory, records):
table = Table()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -191,7 +191,6 @@
Augments for the directory service records to add calendar specific attributes.
A variety of augment services are available for use.
- When using a partitioned server, a service that can be accessed from each host will be needed.
-->
<!-- XML File Augment Service -->
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -203,7 +203,6 @@
Augments for the directory service records to add calendar specific attributes.
A variety of augment services are available for use.
- When using a partitioned server, a service that can be accessed from each host will be needed.
-->
<!-- XML File Augment Service -->
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -195,7 +195,6 @@
Augments for the directory service records to add calendar specific attributes.
A variety of augment services are available for use.
- When using a partitioned server, a service that can be accessed from each host will be needed.
-->
<!-- XML File Augment Service -->
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -22,7 +22,7 @@
from calendarserver.tools.calverify import BadDataService, \
SchedulingMismatchService, DoubleBookingService, DarkPurgeService
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
@@ -511,7 +511,7 @@
"""
sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": True,
@@ -555,7 +555,7 @@
"""
sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": True,
@@ -627,7 +627,7 @@
"""
sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -667,7 +667,7 @@
"""
sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -969,7 +969,7 @@
self.notifierFactory.reset()
-now = PyCalendarDateTime.getToday()
+now = DateTime.getToday()
now.setDay(1)
now.offsetMonth(2)
nowYear = now.getYear()
@@ -1404,7 +1404,7 @@
home = (yield self.homeUnderTest(name=self.uuid3))
calendar = (yield self.calendarUnderTest(name="calendar2", home=self.uuid3))
- yield home.setDefaultCalendar(calendar)
+ yield home.setDefaultCalendar(calendar, "VEVENT")
yield self.commit()
@@ -1418,7 +1418,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1431,7 +1431,7 @@
"uid": "",
"uuid": "",
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -1485,7 +1485,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1498,7 +1498,7 @@
"uid": "",
"uuid": "",
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -1576,7 +1576,7 @@
self.assertNotEqual(sync_token_old3, sync_token_new3)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
yield calverify.doAction()
@@ -1694,7 +1694,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1707,7 +1707,7 @@
"uid": "",
"uuid": "",
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -1745,7 +1745,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1758,7 +1758,7 @@
"uid": "",
"uuid": "",
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -1803,7 +1803,7 @@
self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
yield calverify.doAction()
@@ -1921,7 +1921,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1934,7 +1934,7 @@
"uid": "",
"uuid": CalVerifyMismatchTestsBase.uuidl1,
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -1970,7 +1970,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -1983,7 +1983,7 @@
"uid": "",
"uuid": CalVerifyMismatchTestsBase.uuidl1,
"tzid": "",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2021,7 +2021,7 @@
self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
options["uuid"] = CalVerifyMismatchTestsBase.uuidl1
calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2430,7 +2430,7 @@
sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -2446,7 +2446,7 @@
"uid": "",
"uuid": self.uuidl1,
"tzid": "utc",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
}
output = StringIO()
calverify = DoubleBookingService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2592,7 +2592,7 @@
"""
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -2609,7 +2609,7 @@
"uid": "",
"uuid": self.uuidl1,
"tzid": "utc",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
"no-organizer": False,
"invalid-organizer": False,
"disabled-organizer": False,
@@ -2639,7 +2639,7 @@
"""
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -2656,7 +2656,7 @@
"uid": "",
"uuid": self.uuidl1,
"tzid": "utc",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
"no-organizer": False,
"invalid-organizer": False,
"disabled-organizer": False,
@@ -2678,7 +2678,7 @@
self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
options["uuid"] = self.uuidl1
calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2698,7 +2698,7 @@
"""
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -2715,7 +2715,7 @@
"uid": "",
"uuid": self.uuidl1,
"tzid": "utc",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
"no-organizer": True,
"invalid-organizer": False,
"disabled-organizer": False,
@@ -2737,7 +2737,7 @@
self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
options["uuid"] = self.uuidl1
calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2757,7 +2757,7 @@
"""
sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
- self.commit()
+ yield self.commit()
options = {
"ical": False,
@@ -2774,7 +2774,7 @@
"uid": "",
"uuid": self.uuidl1,
"tzid": "utc",
- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
"no-organizer": True,
"invalid-organizer": True,
"disabled-organizer": True,
@@ -2796,7 +2796,7 @@
self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
# Re-scan after changes to make sure there are no errors
- self.commit()
+ yield self.commit()
options["fix"] = False
options["uuid"] = self.uuidl1
calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -193,6 +193,7 @@
self.assertEquals(record.extras["zip"], "95014")
self.assertEquals(record.extras["country"], "USA")
self.assertEquals(record.extras["phone"], "(408) 555-1212")
+ self.assertEquals(record.extras["geo"], "geo:37.331,-122.030")
results = yield self.runCommand(command_getLocationAttributes)
self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03', 'user04']))
@@ -424,6 +425,8 @@
<string>USA</string>
<key>Phone</key>
<string>(408) 555-1212</string>
+ <key>Geo</key>
+ <string>geo:37.331,-122.030</string>
<key>ReadProxies</key>
<array>
<string>users:user03</string>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,8 +21,8 @@
from twistedcaldav.ical import Component
from twistedcaldav.test.util import StoreTestCase
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twisted.internet.defer import inlineCallbacks
from txdav.common.datastore.test.util import populateCalendarsFrom
@@ -33,11 +33,11 @@
import os
-future = PyCalendarDateTime.getNowUTC()
+future = DateTime.getNowUTC()
future.offsetDay(1)
future = future.getText()
-past = PyCalendarDateTime.getNowUTC()
+past = DateTime.getNowUTC()
past.offsetDay(-1)
past = past.getText()
@@ -230,7 +230,7 @@
def test_cancelRepeating(self):
# A repeating event where purged CUA is organizer
event = Component.fromString(REPEATING_1_ICS_BEFORE)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_MODIFIED)
self.assertEquals(str(event), REPEATING_1_ICS_AFTER)
@@ -239,7 +239,7 @@
def test_cancelAllDayRepeating(self):
# A repeating All Day event where purged CUA is organizer
event = Component.fromString(REPEATING_2_ICS_BEFORE)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_MODIFIED)
self.assertEquals(str(event), REPEATING_2_ICS_AFTER)
@@ -248,7 +248,7 @@
def test_cancelFutureEvent(self):
# A future event
event = Component.fromString(FUTURE_EVENT_ICS)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
@@ -256,7 +256,7 @@
def test_cancelNonMeeting(self):
# A repeating non-meeting event
event = Component.fromString(REPEATING_NON_MEETING_ICS)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
@@ -264,7 +264,7 @@
def test_cancelAsAttendee(self):
# A repeating meeting event where purged CUA is an attendee
event = Component.fromString(REPEATING_ATTENDEE_MEETING_ICS)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
@@ -273,7 +273,7 @@
# A repeating meeting occurrence with no master, where purged CUA is
# an attendee
event = Component.fromString(INVITED_TO_OCCURRENCE_ICS)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:9DC04A71-E6DD-11DF-9492-0800200C9A66")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
@@ -282,7 +282,7 @@
# Multiple meeting occurrences with no master, where purged CUA is
# an attendee
event = Component.fromString(INVITED_TO_MULTIPLE_OCCURRENCES_ICS)
- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
"urn:uuid:9DC04A71-E6DD-11DF-9492-0800200C9A66")
self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,8 +21,8 @@
from calendarserver.tools.purge import PurgeOldEventsService, PurgeAttachmentsService, \
PurgePrincipalService
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twext.enterprise.dal.syntax import Update, Delete
from twext.web2.http_headers import MimeType
@@ -39,7 +39,7 @@
import os
-now = PyCalendarDateTime.getToday().getYear()
+now = DateTime.getToday().getYear()
OLD_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -443,7 +443,7 @@
@inlineCallbacks
def test_eventsOlderThan(self):
- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
txn = self._sqlCalendarStore.newTransaction()
# Query for all old events
@@ -475,7 +475,7 @@
@inlineCallbacks
def test_removeOldEvents(self):
- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
txn = self._sqlCalendarStore.newTransaction()
# Remove oldest event - except we don't know what that is because of the dummy timestamps
@@ -598,7 +598,7 @@
self.assertTrue(os.path.exists(mattachmentPath2))
# Delete all old events (including the event containing the attachment)
- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
count = (yield self.transactionUnderTest().removeOldEvents(cutoff))
# See which events have gone and which exist
@@ -633,7 +633,7 @@
# Dry run
total = (yield PurgeOldEventsService.purgeOldEvents(
self._sqlCalendarStore,
- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
+ DateTime(now, 4, 1, 0, 0, 0),
2,
dryrun=True,
verbose=False
@@ -643,7 +643,7 @@
# Actually remove
total = (yield PurgeOldEventsService.purgeOldEvents(
self._sqlCalendarStore,
- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
+ DateTime(now, 4, 1, 0, 0, 0),
2,
verbose=False
))
@@ -652,7 +652,7 @@
# There should be no more left
total = (yield PurgeOldEventsService.purgeOldEvents(
self._sqlCalendarStore,
- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
+ DateTime(now, 4, 1, 0, 0, 0),
2,
verbose=False
))
@@ -681,7 +681,7 @@
# Purge home1
total, ignored = (yield PurgePrincipalService.purgeUIDs(self._sqlCalendarStore, self.directory,
self.rootResource, ("home1",), verbose=False, proxies=False,
- when=PyCalendarDateTime(now, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
+ when=DateTime(now, 4, 1, 12, 0, 0, 0, Timezone(utc=True))))
# 4 items deleted: 3 events and 1 vcard
self.assertEquals(total, 4)
@@ -768,7 +768,7 @@
# Remove old events first
total = (yield PurgeOldEventsService.purgeOldEvents(
self._sqlCalendarStore,
- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
+ DateTime(now, 4, 1, 0, 0, 0),
2,
verbose=False
))
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/accounts-test-pod.xml)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<accounts realm="Test Realm">
+ <user>
+ <uid>admin</uid>
+ <guid>admin</guid>
+ <password>admin</password>
+ <name>Super User</name>
+ <first-name>Super</first-name>
+ <last-name>User</last-name>
+ </user>
+ <user repeat="101">
+ <uid>user%02d</uid>
+ <uid>User %02d</uid>
+ <guid>user%02d</guid>
+ <password>user%02d</password>
+ <name>User %02d</name>
+ <first-name>User</first-name>
+ <last-name>%02d</last-name>
+ <email-address>user%02d at example.com</email-address>
+ </user>
+ <user repeat="101">
+ <uid>puser%02d</uid>
+ <uid>Puser %02d</uid>
+ <guid>puser%02d</guid>
+ <password>puser%02d</password>
+ <name>Puser %02d</name>
+ <first-name>Puser</first-name>
+ <last-name>%02d</last-name>
+ <email-address>puser%02d at example.com</email-address>
+ </user>
+</accounts>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/augments-test-pod.xml)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE augments SYSTEM "augments.dtd">
+
+<augments>
+ <record>
+ <uid>Default</uid>
+ <enable>true</enable>
+ <server-id>A</server-id>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ </record>
+ <record repeat="101">
+ <uid>puser%02d</uid>
+ <enable>true</enable>
+ <server-id>B</server-id>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ </record>
+</augments>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,7 +19,7 @@
<!ELEMENT record (
uid,
enable,
- (server-id, partition-id?)?,
+ server-id?,
enable-calendar?,
enable-addressbook?,
enable-login?,
@@ -32,7 +32,6 @@
<!ELEMENT uid (#PCDATA)>
<!ELEMENT enable (#PCDATA)>
<!ELEMENT server-id (#PCDATA)>
- <!ELEMENT partition-id (#PCDATA)>
<!ELEMENT enable-calendar (#PCDATA)>
<!ELEMENT enable-addressbook (#PCDATA)>
<!ELEMENT enable-login (#PCDATA)>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/proxies-test-pod.xml)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2013 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.
+ -->
+
+<!DOCTYPE proxies SYSTEM "proxies.dtd">
+
+<proxies>
+</proxies>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/resources-test-pod.xml)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<accounts realm="Test Realm">
+</accounts>
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
-
- <!-- Servers -->
- <key>Servers</key>
- <dict>
- <key>Enabled</key>
- <true/>
- <key>ConfigFile</key>
- <string>localservers.xml</string>
- <key>MaxClients</key>
- <integer>5</integer>
- </dict>
- <key>ServerPartitionID</key>
- <string>00001</string>
-
- <!-- PostgreSQL ProxyDB Service -->
- <key>ProxyDBService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-
- <key>params</key>
- <dict>
- <key>host</key>
- <string>localhost</string>
- <key>database</key>
- <string>proxies</string>
- </dict>
- </dict>
-
- <!-- Support for Memcached -->
- <key>Memcached</key>
- <dict>
- <key>Pools</key>
- <dict>
- <key>CommonToAllNodes</key>
- <dict>
- <key>ClientEnabled</key>
- <true/>
- <key>ServerEnabled</key>
- <true/>
- <key>BindAddress</key>
- <string>localhost</string>
- <key>Port</key>
- <integer>11311</integer>
- <key>HandleCacheTypes</key>
- <array>
- <string>ProxyDB</string>
- <string>PrincipalToken</string>
- <string>DIGESTCREDENTIALS</string>
- </array>
- </dict>
- </dict>
- <key>MaxClients</key>
- <integer>5</integer>
- <key>memcached</key>
- <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
- <key>Options</key>
- <array>
- <!--<string>-vv</string>-->
- </array>
- </dict>
-
- </dict>
-</plist>
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
-
- <!-- Servers -->
- <key>Servers</key>
- <dict>
- <key>Enabled</key>
- <true/>
- <key>ConfigFile</key>
- <string>localservers.xml</string>
- <key>MaxClients</key>
- <integer>5</integer>
- </dict>
- <key>ServerPartitionID</key>
- <string>00002</string>
-
- <!-- PostgreSQL ProxyDB Service -->
- <key>ProxyDBService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-
- <key>params</key>
- <dict>
- <key>host</key>
- <string>localhost</string>
- <key>database</key>
- <string>proxies</string>
- </dict>
- </dict>
-
- <!-- Support for Memcached -->
- <key>Memcached</key>
- <dict>
- <key>Pools</key>
- <dict>
- <key>CommonToAllNodes</key>
- <dict>
- <key>ClientEnabled</key>
- <true/>
- <key>ServerEnabled</key>
- <false/>
- <key>BindAddress</key>
- <string>localhost</string>
- <key>Port</key>
- <integer>11311</integer>
- <key>HandleCacheTypes</key>
- <array>
- <string>ProxyDB</string>
- <string>PrincipalToken</string>
- <string>DIGESTCREDENTIALS</string>
- </array>
- </dict>
- </dict>
- <key>MaxClients</key>
- <integer>5</integer>
- <key>memcached</key>
- <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
- <key>Options</key>
- <array>
- <!--<string>-vv</string>-->
- </array>
- </dict>
-
- </dict>
-</plist>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist (from rev 12016, CalendarServer/trunk/conf/caldavd-test-podA.plist)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2006-2009 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.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+
+ <!-- Import a parent config before this one -->
+ <key>ImportConfig</key>
+ <string>./conf/caldavd-test.plist</string>
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+ <integer>8008</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <key>SSLPort</key>
+ <integer>8443</integer>
+
+ <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+ <key>BindHTTPPorts</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+ <key>BindSSLPorts</key>
+ <array>
+ </array>
+
+ <!-- Server root -->
+ <key>ServerRoot</key>
+ <string>./data/podA</string>
+
+ <!-- Configuration root -->
+ <key>ConfigRoot</key>
+ <string>./conf</string>
+
+ <!-- XML File Directory Service -->
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/accounts-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- Resource and Location Service -->
+ <key>ResourceService</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/resources-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- XML File Augment Service -->
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+ <string>./conf/auth/augments-test-pod.xml</string>
+ </array>
+ </dict>
+ </dict>
+
+ <key>ProxyLoadFromFile</key>
+ <string>./conf/auth/proxies-test-pod.xml</string>
+
+ <!-- Servers -->
+ <key>Servers</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>ConfigFile</key>
+ <string>./conf/localservers-test.xml</string>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>InboxName</key>
+ <string>podding</string>
+ </dict>
+
+ <!-- Support for Memcached -->
+ <key>Memcached</key>
+ <dict>
+ <key>Pools</key>
+ <dict>
+ <key>Default</key>
+ <dict>
+ <key>ClientEnabled</key>
+ <true/>
+ <key>ServerEnabled</key>
+ <true/>
+ <key>BindAddress</key>
+ <string>localhost</string>
+ <key>Port</key>
+ <integer>11211</integer>
+ </dict>
+ <key>ProxyDB</key>
+ <dict>
+ <key>ClientEnabled</key>
+ <true/>
+ <key>ServerEnabled</key>
+ <true/>
+ <key>BindAddress</key>
+ <string>localhost</string>
+ <key>Port</key>
+ <integer>11311</integer>
+ <key>HandleCacheTypes</key>
+ <array>
+ <string>ProxyDB</string>
+ <string>PrincipalToken</string>
+ <string>DIGESTCREDENTIALS</string>
+ </array>
+ </dict>
+ </dict>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>memcached</key>
+ <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
+ <key>Options</key>
+ <array>
+ <!--<string>-vv</string>-->
+ </array>
+ </dict>
+
+ </dict>
+</plist>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist (from rev 12016, CalendarServer/trunk/conf/caldavd-test-podB.plist)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2006-2009 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.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+
+ <!-- Import a parent config before this one -->
+ <key>ImportConfig</key>
+ <string>./conf/caldavd-test.plist</string>
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+ <integer>8108</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <key>SSLPort</key>
+ <integer>8543</integer>
+
+ <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+ <key>BindHTTPPorts</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+ <key>BindSSLPorts</key>
+ <array>
+ </array>
+
+ <!-- Server root -->
+ <key>ServerRoot</key>
+ <string>./data/podB</string>
+
+ <!-- Configuration root -->
+ <key>ConfigRoot</key>
+ <string>./conf</string>
+
+ <!-- XML File Directory Service -->
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/accounts-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- Resource and Location Service -->
+ <key>ResourceService</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/resources-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- XML File Augment Service -->
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+ <string>./conf/auth/augments-test-pod.xml</string>
+ </array>
+ </dict>
+ </dict>
+
+ <key>ProxyLoadFromFile</key>
+ <string>./conf/auth/proxies-test-pod.xml</string>
+
+ <!-- Servers -->
+ <key>Servers</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>ConfigFile</key>
+ <string>./conf/localservers-test.xml</string>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>InboxName</key>
+ <string>podding</string>
+ </dict>
+
+ <!-- Support for Memcached -->
+ <key>Memcached</key>
+ <dict>
+ <key>Pools</key>
+ <dict>
+ <key>Default</key>
+ <dict>
+ <key>ClientEnabled</key>
+ <true/>
+ <key>ServerEnabled</key>
+ <true/>
+ <key>BindAddress</key>
+ <string>localhost</string>
+ <key>Port</key>
+ <integer>11411</integer>
+ </dict>
+ <key>ProxyDB</key>
+ <dict>
+ <key>ClientEnabled</key>
+ <true/>
+ <key>ServerEnabled</key>
+ <true/>
+ <key>BindAddress</key>
+ <string>localhost</string>
+ <key>Port</key>
+ <integer>11311</integer>
+ <key>HandleCacheTypes</key>
+ <array>
+ <string>ProxyDB</string>
+ <string>PrincipalToken</string>
+ <string>DIGESTCREDENTIALS</string>
+ </array>
+ </dict>
+ </dict>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>memcached</key>
+ <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
+ <key>Options</key>
+ <array>
+ <!--<string>-vv</string>-->
+ </array>
+ </dict>
+
+ </dict>
+</plist>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -895,9 +895,10 @@
<key>EnableWebAdmin</key>
<true/>
- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+ <!-- Support for Content-Encoding compression -->
<key>ResponseCompression</key>
- <false/>
+ <false/> <!-- Off for testing, as debugging is easier that way. -->
+
<!-- The retry-after value (in seconds) to return with a 503 error. -->
<key>HTTPRetryAfter</key>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,21 +16,19 @@
limitations under the License.
-->
-<!DOCTYPE servers SYSTEM "servers.dtd">
+<!DOCTYPE servers SYSTEM "localservers.dtd">
<servers>
<server>
- <id>00001</id>
+ <id>A</id>
<uri>http://localhost:8008</uri>
- <partitions>
- <partition>
- <id>00001</id>
- <uri>http://localhost:8008</uri>
- </partition>
- <partition>
- <id>00002</id>
- <uri>http://localhost:8108</uri>
- </partition>
- </partitions>
+ <allowed-from>localhost</allowed-from>
+ <shared-secret>A</shared-secret>
</server>
+ <server>
+ <id>B</id>
+ <uri>http://localhost:8108</uri>
+ <allowed-from>localhost</allowed-from>
+ <shared-secret>B</shared-secret>
+ </server>
</servers>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd (from rev 12016, CalendarServer/trunk/conf/localservers.dtd)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,25 @@
+<!--
+Copyright (c) 2011-2013 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.
+-->
+
+<!ELEMENT servers (server*) >
+
+ <!ELEMENT server (id, uri, allowed-from*, shared-secret?) >
+ <!ATTLIST server implicit (yes|no) "yes">
+
+ <!ELEMENT id (#PCDATA) >
+ <!ELEMENT uri (#PCDATA) >
+ <!ELEMENT allowed-from (#PCDATA) >
+ <!ELEMENT shared-secret (#PCDATA) >
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
limitations under the License.
-->
-<!DOCTYPE servers SYSTEM "servers.dtd">
+<!DOCTYPE servers SYSTEM "localservers.dtd">
<servers>
<!--
@@ -30,16 +30,6 @@
<allowed-from>127.0.0.1</allowed-from>
<allowed-from>example.local</allowed-from>
<shared-secret>ABC</shared-secret>
- <partitions>
- <partition>
- <id>00001</id>
- <url>https://machine1.example.com:8443</url>
- </partition>
- <partition>
- <id>00002</id>
- <url>https://machine2.example.com:8443</url>
- </partition>
- </partitions>
</server>
-->
</servers>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
limitations under the License.
-->
-<!DOCTYPE servers SYSTEM "servertoserver.dtd">
+<!DOCTYPE servers SYSTEM "remoteservers.dtd">
<servers>
<server>
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd (from rev 12016, CalendarServer/trunk/conf/remoteservers.dtd)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,33 @@
+<!--
+Copyright (c) 2006-2013 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.
+-->
+
+<!ELEMENT servers (server*) >
+
+ <!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) >
+
+ <!ELEMENT uri (#PCDATA) >
+ <!ELEMENT authentication (user, password) >
+ <!ATTLIST authentication type (basic) "">
+ <!ELEMENT user (#PCDATA) >
+ <!ELEMENT password (#PCDATA) >
+
+ <!ELEMENT allow-requests-from EMPTY >
+ <!ELEMENT allow-requests-to EMPTY >
+ <!ELEMENT domains (domain*) >
+ <!ELEMENT domain (#PCDATA) >
+ <!ELEMENT hosts (host*) >
+ <!ELEMENT host (#PCDATA) >
+
\ No newline at end of file
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
limitations under the License.
-->
-<!DOCTYPE servers SYSTEM "servertoserver.dtd">
+<!DOCTYPE servers SYSTEM "remoteservers.dtd">
<servers>
<!--
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -174,7 +174,6 @@
Augments for the directory service records to add calendar specific attributes.
A variety of augment services are available for use.
- When using a partitioned server, a service that can be accessed from each host will be needed.
-->
<!-- XML File Augment Service -->
@@ -670,7 +669,7 @@
<key>EnableWebAdmin</key>
<true/>
- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+ <!-- Support for Content-Encoding compression -->
<key>ResponseCompression</key>
<false/>
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,28 +0,0 @@
-<!--
-Copyright (c) 2011-2013 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.
--->
-
-<!ELEMENT servers (server*) >
-
- <!ELEMENT server (id, uri, allowed-from*, shared-secret?, partitions?) >
- <!ATTLIST server implicit (yes|no) "yes">
-
- <!ELEMENT id (#PCDATA) >
- <!ELEMENT uri (#PCDATA) >
- <!ELEMENT allowed-from (#PCDATA) >
- <!ELEMENT shared-secret (#PCDATA) >
-
- <!ELEMENT partitions (partition*) >
- <!ELEMENT partition (id, uri) >
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,33 +0,0 @@
-<!--
-Copyright (c) 2006-2013 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.
--->
-
-<!ELEMENT servers (server*) >
-
- <!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) >
-
- <!ELEMENT uri (#PCDATA) >
- <!ELEMENT authentication (user, password) >
- <!ATTLIST authentication type (basic) "">
- <!ELEMENT user (#PCDATA) >
- <!ELEMENT password (#PCDATA) >
-
- <!ELEMENT allow-requests-from EMPTY >
- <!ELEMENT allow-requests-to EMPTY >
- <!ELEMENT domains (domain*) >
- <!ELEMENT domain (#PCDATA) >
- <!ELEMENT hosts (host*) >
- <!ELEMENT host (#PCDATA) >
-
\ No newline at end of file
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-<key>users</key>
-<array>
-<!-- Sudo user definitions -->
-<!-- With the exception of username and password none of the following
- elements are used in the current implementation. -->
-<!--
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true/>
- <key>principals</key>
- <array>
- <string>all</string>
- <string>/principals/user/wsanchez</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
-
- <key>username</key>
- <string></string>
-
- <key>password</key>
- <string></string>
- </dict>
--->
- <dict>
- <key>username</key>
- <string>superuser</string>
- <key>password</key>
- <string>superuser</string>
- </dict>
-</array>
-</dict>
-</plist>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -29,9 +29,9 @@
from contrib.performance.httpclient import StringProducer, readBody
from contrib.performance.loadtest.subscribe import Periodical
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
from twext.internet.adaptendpoint import connect
from twext.internet.gaiendpoint import GAIEndpoint
@@ -1199,7 +1199,7 @@
connect(GAIEndpoint(self.reactor, host, port), factory)
- def _receivedPush(self, inboundID, dataChangedTimestamp):
+ def _receivedPush(self, inboundID, dataChangedTimestamp, priority=5):
for href, id in self.ampPushKeys.iteritems():
if inboundID == id:
self._checkCalendarsForEvents(href, push=True)
@@ -1609,13 +1609,13 @@
msg("Availability request spanning multiple days (%r to %r), "
"dropping the end date." % (start, end))
- start.setTimezone(PyCalendarTimezone(utc=True))
+ start.setTimezone(Timezone(utc=True))
start.setHHMMSS(0, 0, 0)
- end = start + PyCalendarDuration(hours=24)
+ end = start + Duration(hours=24)
start = start.getText()
end = end.getText()
- now = PyCalendarDateTime.getNowUTC().getText()
+ now = DateTime.getNowUTC().getText()
label_suffix = "small"
if len(users) > 5:
@@ -1919,7 +1919,7 @@
# the sim can fire a PUT between the PROPFIND and when process the removals.
old_hrefs = set([calendar.url + child for child in calendar.events.keys()])
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
now.setDateOnly(True)
now.offsetMonth(-1) # 1 month back default
result = yield self._report(
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,8 +42,8 @@
from contrib.performance.loadtest.logger import SummarizingMixin
from contrib.performance.loadtest.ical import IncorrectResponseCode
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
class ProfileBase(object):
"""
@@ -407,9 +407,9 @@
vevent = vcalendar.mainComponent()
uid = str(uuid4())
dtstart = self._eventStartDistribution.sample()
- dtend = dtstart + PyCalendarDuration(seconds=self._eventDurationDistribution.sample())
- vevent.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vevent.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
+ vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
vevent.replaceProperty(Property("DTSTART", dtstart))
vevent.replaceProperty(Property("DTEND", dtend))
vevent.replaceProperty(Property("UID", uid))
@@ -650,9 +650,9 @@
vevent = vcalendar.mainComponent()
uid = str(uuid4())
dtstart = self._eventStartDistribution.sample()
- dtend = dtstart + PyCalendarDuration(seconds=self._eventDurationDistribution.sample())
- vevent.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vevent.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
+ vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
vevent.replaceProperty(Property("DTSTART", dtstart))
vevent.replaceProperty(Property("DTEND", dtend))
vevent.replaceProperty(Property("UID", uid))
@@ -719,8 +719,8 @@
vtodo = vcalendar.mainComponent()
uid = str(uuid4())
due = self._taskStartDistribution.sample()
- vtodo.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vtodo.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ vtodo.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vtodo.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
vtodo.replaceProperty(Property("DUE", due))
vtodo.replaceProperty(Property("UID", uid))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -346,6 +346,7 @@
if 'serverStats' in config:
if config['serverStats']['enabled']:
serverStats = config['serverStats']
+ serverStats['server'] = config['server'] if 'server' in config else ''
observers = []
if 'observers' in config:
@@ -483,7 +484,7 @@
"""
if self.serverStats is not None:
- _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.server)
+ _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.serverStats["server"])
data = self.readStatsSock((hostname.split(":")[0], self.serverStats["Port"],), True)
if "Failed" not in data:
data = data["5 Minutes"]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,8 +24,8 @@
from contrib.performance.loadtest.ical import XMPPPush, Event, Calendar, OS_X_10_6
from contrib.performance.loadtest.sim import _DirectoryRecord
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
from twisted.internet.protocol import ProtocolToConsumerAdapter
@@ -1957,8 +1957,8 @@
self.client.outbox = "/calendars/__uids__/%s/outbox/" % (self.record.uid,)
requests = self.interceptRequests()
- start = PyCalendarDateTime(2011, 6, 10, 10, 45, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2011, 6, 10, 11, 15, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(2011, 6, 10, 10, 45, 0, tzid=Timezone(utc=True))
+ end = DateTime(2011, 6, 10, 11, 15, 0, tzid=Timezone(utc=True))
d = self.client.requestAvailability(
start, end, [u"urn:uuid:user05", u"urn:uuid:user10"])
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
from caldavclientlibrary.protocol.url import URL
from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.web2.dav.util import joinURL
from caldavclientlibrary.protocol.webdav.definitions import davxml
@@ -67,7 +67,7 @@
"""
# Invite as user02
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
href = joinURL(self.sessions[1].calendarHref, "organizer.ics")
self.sessions[1].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
from caldavclientlibrary.protocol.url import URL
from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.web2.dav.util import joinURL
ICAL = """BEGIN:VCALENDAR
@@ -62,7 +62,7 @@
Execute the actual HTTP request.
"""
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
href = joinURL(self.sessions[0].calendarHref, "put.ics")
self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,7 +18,7 @@
from caldavclientlibrary.protocol.webdav.definitions import davxml, statuscodes
from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
from twext.web2.dav.util import joinURL
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from caldavclientlibrary.protocol.caldav.query import QueryVEVENTTimeRange
from caldavclientlibrary.protocol.http.data.string import ResponseDataString
@@ -70,7 +70,7 @@
Do some setup prior to the real request.
"""
# Add resources to create required number of changes
- self.start = PyCalendarDateTime.getNowUTC()
+ self.start = DateTime.getNowUTC()
self.start.setHHMMSS(12, 0, 0)
self.end = self.start.duplicate()
self.end.offsetHours(1)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,7 +18,7 @@
from caldavclientlibrary.protocol.webdav.definitions import davxml
from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
from twext.web2.dav.util import joinURL
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
ICAL = """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
@@ -75,7 +75,7 @@
self.synctoken = results[davxml.sync_token]
# Add resources to create required number of changes
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
for i in range(self.count):
href = joinURL(self.sessions[0].calendarHref, "sync-collection-%d.ics" % (i + 1,))
self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1, i + 1,), "text/calendar")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -27,7 +27,7 @@
from contrib.performance.sqlusage.requests.put import PutTest
from contrib.performance.sqlusage.requests.query import QueryTest
from contrib.performance.sqlusage.requests.sync import SyncTest
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.web2.dav.util import joinURL
import getopt
import itertools
@@ -183,7 +183,7 @@
@param n: number of events
@type n: C{int}
"""
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
for i in range(n - self.currentCount):
index = self.currentCount + i + 1
href = joinURL(calendarhref, "%d.ics" % (index,))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,10 +16,10 @@
from __future__ import print_function
from math import log, sqrt
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.property import PyCalendarProperty
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration as PyDuration
+from pycalendar.icalendar.property import Property
+from pycalendar.timezone import Timezone
from twisted.python.util import FancyEqMixin
from zope.interface import Interface, implements
import random
@@ -338,7 +338,7 @@
def sample(self):
- now = PyCalendarDateTime.getNowUTC()
+ now = DateTime.getNowUTC()
now.offsetSeconds(int(self._offset.sample()))
return now
@@ -390,7 +390,7 @@
60 * 60 * 8 * 6,
# Standard deviation of 4 workdays
60 * 60 * 8 * 4)
- self.now = PyCalendarDateTime.getNow
+ self.now = DateTime.getNow
def astimestamp(self, dt):
@@ -406,7 +406,7 @@
# Find a workday that follows the timestamp
weekday = when.getDayOfWeek()
for i in range(NUM_WEEKDAYS):
- day = when + PyCalendarDuration(days=i)
+ day = when + PyDuration(days=i)
if (weekday + i) % NUM_WEEKDAYS in self._daysOfWeek:
# Joy, a day on which work might occur. Find the first hour on
# this day when work may start.
@@ -419,8 +419,8 @@
def sample(self):
- offset = PyCalendarDuration(seconds=int(self._helperDistribution.sample()))
- beginning = self.now(PyCalendarTimezone(tzid=self._tzname))
+ offset = PyDuration(seconds=int(self._helperDistribution.sample()))
+ beginning = self.now(Timezone(tzid=self._tzname))
while offset:
start, end = self._findWorkAfter(beginning)
if end - start > offset:
@@ -463,8 +463,7 @@
index = self._helperDistribution.sample()
rrule = self._rrules[index]
if rrule:
- prop = PyCalendarProperty()
- prop.parse(rrule)
+ prop = Property.parseText(rrule)
return prop
return None
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,8 +20,8 @@
SQLDuration, LogNormalDistribution, UniformDiscreteDistribution,
UniformIntegerDistribution, WorkDistribution, quantize,
RecurrenceDistribution)
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
class SQLDurationTests(TestCase):
def setUp(self):
@@ -90,21 +90,21 @@
tzname = "US/Eastern"
dist = WorkDistribution(["mon", "wed", "thu", "sat"], 10, 20, tzname)
dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
- dist.now = lambda tzname = None: PyCalendarDateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
+ dist.now = lambda tzname = None: DateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
value = dist.sample()
self.assertEqual(
# Move past three workdays - monday, wednesday, thursday - using 30
# of the hours, and then five and a half hours into the fourth
# workday, saturday. Workday starts at 10am, so the sample value
# is 3:30pm, ie 1530 hours.
- PyCalendarDateTime(2011, 6, 4, 15, 30, 0, tzid=PyCalendarTimezone(tzid=tzname)),
+ DateTime(2011, 6, 4, 15, 30, 0, tzid=Timezone(tzid=tzname)),
value
)
dist = WorkDistribution(["mon", "tue", "wed", "thu", "fri"], 10, 20, tzname)
dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
value = dist.sample()
- self.assertTrue(isinstance(value, PyCalendarDateTime))
+ self.assertTrue(isinstance(value, DateTime))
# twisted.trial.unittest.FailTest: not equal:
# a = datetime.datetime(2011, 6, 4, 15, 30, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -303,8 +303,6 @@
print("--procs N specifies how many python processes are expected in the log file (default: 80)")
print("--top N how many long requests to print (default: 10)")
print("--users N how many top users to print (default: 5)")
- print("--router analyze a partition server router node")
- print("--worker analyze a partition server worker node")
print("")
print("Version: 5")
@@ -313,19 +311,13 @@
numTop = 10
numUsers = 5
lineRange = None
-router = False
-worker = False
-options, args = getopt.getopt(sys.argv[1:], "h", ["debug", "router", "worker", "lines=", "range=", "procs=", "top=", "users="])
+options, args = getopt.getopt(sys.argv[1:], "h", ["debug", "lines=", "range=", "procs=", "top=", "users="])
for option, value in options:
if option == "-h":
usage()
sys.exit(0)
elif option == "--debug":
debug = True
- elif option == "--router":
- router = True
- elif option == "--worker":
- worker = True
elif option == "--lines":
numLines = int(value)
elif option == "--range":
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,7 +20,7 @@
import os
import sys
import traceback
-from pycalendar.calendar import PyCalendar
+from pycalendar.icalendar.calendar import Calendar
def usage(error_msg=None):
if error_msg:
@@ -72,7 +72,7 @@
print("Path does not exist: '%s'. Ignoring." % (arg,))
continue
- cal = PyCalendar()
+ cal = Calendar()
cal.parse(open(arg))
print(str(cal.serialize()))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -145,7 +145,6 @@
"bin/calendarserver_export",
#"bin/calendarserver_icalendar_validate",
#"bin/calendarserver_load_augmentdb",
- #"bin/calendarserver_make_partition",
#"bin/calendarserver_manage_postgres",
"bin/calendarserver_manage_principals",
"bin/calendarserver_manage_push",
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh 2013-12-14 06:28:16 UTC (rev 12110)
@@ -40,11 +40,67 @@
fi;
}
+# Checks for presence of a C header, optionally with a version comparison.
+# With only a header file name, try to include it, returning nonzero if absent.
+# With 3 params, also attempt a version check, returning nonzero if too old.
+# Param 2 is a minimum acceptable version number
+# Param 3 is a #define from the source that holds the installed version number
+# Examples:
+# Assert that ldap.h is present
+# find_header "ldap.h"
+# Assert that ldap.h is present with a version >= 20344
+# find_header "ldap.h" 20344 "LDAP_VENDOR_VERSION"
find_header () {
- local sysheader="$1"; shift;
- echo "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
- return "$?";
-}
+ ARGS="$@";
+ ret=1; # default to a failed check, forcing a fetch of the depencency
+ i=0;
+ for a in $ARGS; do
+ [ $i -eq 0 ] && local sysheader="$1";
+ [ $i -eq 1 ] && local minver="$2";
+ [ $i -eq 2 ] && local def="$3";
+ i=$(($i+1));
+ done;
+ [ ! $sysheader ] && return 1;
+ # Check for presence of a header. We use the "-c" cc option because we don't
+ # need to emit a file; cc exits nonzero if it can't find the header
+ if [ $# -lt 2 ]; then
+ echo "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
+ return "$?";
+ # Check for presence of a header of specified version
+ else
+ found='';
+ local aout=$(mktemp -t ccXXXXXX); # compiled executable file path
+ local prog=$(mktemp -t ccXXXXXX); # C source file path
+ cat <<DOC > ${prog}
+#include <${sysheader}>
+#include <stdio.h>
+#define STR(x) #x
+#define SHOW_DEFINE(x) printf("%s", STR(x))
+int main()
+{
+ if (${def})
+ {
+ SHOW_DEFINE(${def});
+ return 0;
+ };
+ return 1;
+};
+DOC
+ cc -x c -o ${aout} ${prog} &> /dev/null;
+ if [ $? -eq 0 ] && [ -e ${aout} ] ; then
+ found=$(${aout});
+ fi;
+ if [ $? -eq 0 ] && [ ! -z ${found} ] ; then
+ cmp_version $minver $found;
+ ret=$?;
+ else
+ ret=1; #cc exited nonzero or didn't emit a file
+ fi;
+ rm -f "${aout}";
+ rm -f "${prog}";
+ fi;
+ return $ret;
+};
# Initialize all the global state required to use this library.
init_build () {
@@ -213,10 +269,13 @@
if "${force_setup}" || [ ! -d "${path}" ]; then
local ext="$(echo "${url}" | sed 's|^.*\.\([^.]*\)$|\1|')";
+ untar () { tar -xvf -; }
+ unzipstream () { tmp="$(mktemp -t ccsXXXXX)"; cat > "${tmp}"; unzip "${tmp}"; rm "${tmp}"; }
case "${ext}" in
- gz|tgz) decompress="gzip -d -c"; ;;
- bz2) decompress="bzip2 -d -c"; ;;
- tar) decompress="cat"; ;;
+ gz|tgz) decompress="gzip -d -c"; unpack="untar"; ;;
+ bz2) decompress="bzip2 -d -c"; unpack="untar"; ;;
+ tar) decompress="untar"; unpack="untar"; ;;
+ zip) decompress="cat"; unpack="unzipstream"; ;;
*)
echo "Error in www_get of URL ${url}: Unknown extension ${ext}";
exit 1;
@@ -228,7 +287,7 @@
if [ -n "${cache_deps}" ] && [ -n "${hash}" ]; then
mkdir -p "${cache_deps}";
- local cache_basename="${name}-$(echo "${url}" | hash)-$(basename "${url}")";
+ local cache_basename="$(echo ${name} | tr '[ ]' '_')-$(echo "${url}" | hash)-$(basename "${url}")";
local cache_file="${cache_deps}/${cache_basename}";
check_hash () {
@@ -327,7 +386,7 @@
rm -rf "${path}";
cd "$(dirname "${path}")";
- get | ${decompress} | tar -xvf -;
+ get | ${decompress} | ${unpack};
apply_patches "${name}" "${path}";
cd /;
fi;
@@ -670,12 +729,12 @@
if type -P memcached > /dev/null; then
using_system "memcached";
else
- local le="libevent-2.0.17-stable";
- local mc="memcached-1.4.13";
- c_dependency -m "dad64aaaaff16b5fbec25160c06fee9a" \
+ local le="libevent-2.0.21-stable";
+ local mc="memcached-1.4.15";
+ c_dependency -m "b2405cc9ebf264aa47ff615d9de527a2" \
"libevent" "${le}" \
- "https://github.com/downloads/libevent/libevent/${le}.tar.gz";
- c_dependency -m "6d18c6d25da945442fcc1187b3b63b7f" \
+ "http://github.com/downloads/libevent/libevent/${le}.tar.gz";
+ c_dependency -m "36ea966f5a29655be1746bf4949f7f69" \
"memcached" "${mc}" \
"http://memcached.googlecode.com/files/${mc}.tar.gz";
fi;
@@ -683,8 +742,9 @@
if type -P postgres > /dev/null; then
using_system "Postgres";
else
- local pgv="9.2.4";
- local pg="postgresql-${pgv}";
+ local v="9.3.1";
+ local n="postgresql";
+ local p="${n}-${v}";
if type -P dtrace > /dev/null; then
local enable_dtrace="--enable-dtrace";
@@ -692,19 +752,22 @@
local enable_dtrace="";
fi;
- c_dependency -m "52df0a9e288f02d7e6e0af89ed4dcfc6" \
- "PostgreSQL" "${pg}" \
- "ftp://ftp5.us.postgresql.org/pub/PostgreSQL/source/v${pgv}/${pg}.tar.gz" \
+ c_dependency -m "c003d871f712d4d3895956b028a96e74" \
+ "PostgreSQL" "${p}" \
+ "http://ftp.postgresql.org/pub/source/v${v}/${p}.tar.bz2" \
--with-python ${enable_dtrace};
:;
fi;
- if find_header ldap.h; then
+ if find_header ldap.h 20428 LDAP_VENDOR_VERSION; then
using_system "OpenLDAP";
else
- c_dependency -m "ec63f9c2add59f323a0459128846905b" \
- "OpenLDAP" "openldap-2.4.25" \
- "http://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.4.25.tgz" \
+ local v="2.4.38";
+ local n="openldap";
+ local p="${n}-${v}";
+ c_dependency -m "39831848c731bcaef235a04e0d14412f" \
+ "OpenLDAP" "${p}" \
+ "http://www.openldap.org/software/download/OpenLDAP/${n}-release/${p}.tgz" \
--disable-bdb --disable-hdb;
fi;
@@ -726,24 +789,24 @@
# Sourceforge mirror hostname.
local sf="superb-sea2.dl.sourceforge.net";
- local st="setuptools-0.6c11";
+ local st="setuptools-1.4";
local pypi="http://pypi.python.org/packages/source";
- py_dependency -m "7df2a529a074f613b509fb44feefe74e" \
+ py_dependency -v 1 -m "5710464bc5a61d75f5087f15ce63cfe0" \
"setuptools" "setuptools" "${st}" \
"$pypi/s/setuptools/${st}.tar.gz";
- local v="4.0.3";
+ local v="4.0.5";
local n="zope.interface";
local p="${n}-${v}";
- py_dependency -v 4 -m "1ddd308f2c83703accd1696158c300eb" \
+ py_dependency -v 4 -m "caf26025ae1b02da124a58340e423dfe" \
"Zope Interface" "${n}" "${p}" \
- "http://pypi.python.org/packages/source/z/${n}/${p}.tar.gz";
+ "http://pypi.python.org/packages/source/z/${n}/${p}.zip";
- local v="0.10";
+ local v="0.12";
local n="pyOpenSSL";
local p="${n}-${v}";
- py_dependency -v 0.9 -m "34db8056ec53ce80c7f5fc58bee9f093" \
+ py_dependency -v 0.12 -m "60a7bbb6160950823eddcbba2cbcb0d6" \
"${n}" "OpenSSL" "${p}" \
"http://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
@@ -754,18 +817,18 @@
"${svn_uri_base}/${n}/trunk";
fi;
- local v="0.6.1";
+ local v="0.6.4";
local n="xattr";
local p="${n}-${v}";
- py_dependency -v 0.5 -r 1038 \
- "${n}" "${n}" "${n}" \
- "http://svn.red-bean.com/bob/${n}/releases/${p}/";
+ py_dependency -v 0.6 -m "1bef31afb7038800f8d5cfa2f4562b37" \
+ "${n}" "${n}" "${p}" \
+ "${pypi}/x/${n}/${n}-${v}.tar.gz";
if [ -n "${ORACLE_HOME:-}" ]; then
- local v="5.1";
+ local v="5.1.2";
local n="cx_Oracle";
local p="${n}-${v}";
- py_dependency -v "${v}" -m "d2697493a40c9d46c9b7c1c210b61671" \
+ py_dependency -v "${v}" -m "462f309e00f7bff7100e2077fc43172c" \
"${n}" "${n}" "${p}" \
"http://${sf}/project/cx-oracle/${v}/${p}.tar.gz";
fi;
@@ -779,10 +842,10 @@
# Maintenance note: next time the Twisted dependency gets updated, check out
# twext/patches.py.
- local v="12.3.0";
+ local v="13.2.0";
local n="Twisted";
local p="${n}-${v}";
- py_dependency -v 12.2 -m "6e289825f3bf5591cfd670874cc0862d" \
+ py_dependency -v 13.2 -m "83fe6c0c911cc1602dbffb036be0ba79" \
"${n}" "twisted" "${p}" \
"${pypi}/T/${n}/${p}.tar.bz2";
@@ -793,22 +856,22 @@
"${n}" "dateutil" "${p}" \
"http://www.labix.org/download/${n}/${p}.tar.gz";
- local v="0.6.1";
+ local v="1.2.0";
local n="psutil";
local p="${n}-${v}";
- py_dependency -m "3cfcbfb8525f6e4c70110e44a85e907e" \
+ py_dependency -m "f8ae906249e65db21f17d873ae07e584" \
"${n}" "${n}" "${p}" \
- "http://${n}.googlecode.com/files/${p}.tar.gz";
+ "${pypi}/p/${n}/${p}.tar.gz";
- local v="2.3.13";
+ local v="2.4.13";
local n="python-ldap";
local p="${n}-${v}";
- py_dependency -v "${v}" -m "895223d32fa10bbc29aa349bfad59175" \
+ py_dependency -v "${v}" -m "74b7b50267761540451eade44b2049ee" \
"Python-LDAP" "ldap" "${p}" \
"${pypi}/p/${n}/${p}.tar.gz";
# XXX actually PyCalendar should be imported in-place.
- py_dependency -fe -i "src" -r 11458 \
+ py_dependency -fe -i "src" -r 11947 \
"PyCalendar" "pycalendar" "pycalendar" \
"${svn_uri_base}/PyCalendar/trunk";
@@ -840,44 +903,45 @@
"${svn_uri_base}/CalDAVClientLibrary/trunk";
# Can't add "-v 2011g" to args because the version check expects numbers.
+ local v="2013.8";
local n="pytz";
- local p="${n}-2011n";
- py_dependency -m "75ffdc113a4bcca8096ab953df746391" \
+ local p="${n}-${v}";
+ py_dependency -m "37750ca749ed3a52523b9682b0b7e381" \
"${n}" "${n}" "${p}" \
"${pypi}/p/${n}/${p}.tar.gz";
- local v="2.5";
+ local v="2.6.1";
local n="pycrypto";
local p="${n}-${v}";
- py_dependency -v "${v}" -m "783e45d4a1a309e03ab378b00f97b291" \
+ py_dependency -v "${v}" -m "55a61a054aa66812daf5161a0d5d7eda" \
"PyCrypto" "${n}" "${p}" \
"http://ftp.dlitz.net/pub/dlitz/crypto/${n}/${p}.tar.gz";
- local v="0.1.2";
+ local v="0.1.7";
local n="pyasn1";
local p="${n}-${v}";
- py_dependency -v "${v}" -m "a7c67f5880a16a347a4d3ce445862a47" \
+ py_dependency -v "${v}" -m "2cbd80fcd4c7b1c82180d3d76fee18c8" \
"${n}" "${n}" "${p}" \
"${pypi}/p/${n}/${p}.tar.gz";
- local v="1.1.6";
+ local v="1.1.8";
local n="setproctitle";
local p="${n}-${v}";
- py_dependency -v "1.0" -m "1e42e43b440214b971f4b33c21eac369" \
+ py_dependency -v "1.0" -m "728f4c8c6031bbe56083a48594027edd" \
"${n}" "${n}" "${p}" \
"${pypi}/s/${n}/${p}.tar.gz";
- local v="0.6";
+ local v="0.8";
local n="cffi";
local p="${n}-${v}";
- py_dependency -v "0.6" -m "5be33b1ab0247a984d42b27344519337" \
+ py_dependency -v "0.6" -m "e61deb0515311bb42d5d58b9403bc923" \
"${n}" "${n}" "${p}" \
"${pypi}/c/${n}/${p}.tar.gz";
- local v="2.09.1";
+ local v="2.10";
local n="pycparser";
local p="${n}-${v}";
- py_dependency -v "0.6" -m "74aa075fc28b7c24a4426574d1ac91e0" \
+ py_dependency -v "0.6" -m "d87aed98c8a9f386aa56d365fe4d515f" \
"${n}" "${n}" "${p}" \
"${pypi}/p/${n}/${p}.tar.gz";
@@ -889,21 +953,21 @@
local p="${n}-${v}";
py_dependency -o -m "36407974bd5da2af00bf90ca27feeb44" \
"Epydoc" "${n}" "${p}" \
- "https://pypi.python.org/packages/source/e/${n}/${p}.tar.gz";
+ "${pypi}/e/${n}/${p}.tar.gz";
local v="0.10.0";
local n="Nevow";
local p="${n}-${v}";
py_dependency -o -m "66dda2ad88f42dea05911add15f4d1b2" \
"${n}" "${n}" "${p}" \
- "https://pypi.python.org/packages/source/N/${n}/${p}.tar.gz";
+ "${pypi}/N/${n}/${p}.tar.gz";
- local v="0.4";
+ local v="0.5b1";
local n="pydoctor";
local p="${n}-${v}";
- py_dependency -o -m "b7564e12b5d35d4cb529a2c220b25d3a" \
+ py_dependency -o -m "c4fb33672f37624116cc7a0606f74f28" \
"${n}" "${n}" "${p}" \
- "https://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
+ "{$pypi}/p/${n}/${p}.tar.gz";
if "${do_setup}"; then
cd "${caldav}";
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -26,7 +26,7 @@
# Compute the version number.
#
- base_version = "5.2"
+ base_version = "6.0"
branches = tuple(
branch.format(version=base_version)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/test
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/test 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/test 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,7 +21,7 @@
wd="$(cd "$(dirname "$0")" && pwd -L)";
-. "${wd}/support/build.sh";
+#. "${wd}/support/build.sh";
do_setup="false";
do_get="false";
@@ -74,9 +74,6 @@
export PYTHONPATH="${wd}:${PYTHONPATH:-}";
-dependencies;
-trial="$(type -p trial)";
-
if [ $# -gt 0 ]; then
test_modules="$@";
flaky=true;
@@ -88,7 +85,7 @@
find "${wd}" -name \*.pyc -print0 | xargs -0 rm;
mkdir -p "${wd}/data";
-cd "${wd}" && "${python}" "${trial}" --temp-directory="${wd}/data/trial" --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
+cd "${wd}" && "${wd}/bin/trial" --temp-directory="${wd}/data/trial" --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
if ${flaky}; then
echo "";
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1686,6 +1686,7 @@
SQLFragment(' in %s mode' % (self.mode,)))
+
class DatabaseLock(_LockingStatement):
"""
An SQL exclusive session level advisory lock
@@ -1706,6 +1707,7 @@
return succeed(None)
+
class DatabaseUnlock(_LockingStatement):
"""
An SQL exclusive session level advisory lock
@@ -1726,6 +1728,7 @@
return succeed(None)
+
class Savepoint(_LockingStatement):
"""
An SQL 'savepoint' statement.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -76,8 +76,8 @@
def resultOf(deferred, propagate=False):
"""
- Add a callback and errback which will capture the result of a L{Deferred} in
- a list, and return that list. If 'propagate' is True, pass through the
+ Add a callback and errback which will capture the result of a L{Deferred}
+ in a list, and return that list. If 'propagate' is True, pass through the
results.
"""
results = []
@@ -194,7 +194,6 @@
No implementation.
"""
-
def callFromThread(self, thunk, *a, **kw):
"""
No implementation.
@@ -223,14 +222,16 @@
self.factory = ConnectionFactory()
connect = self.factory.connect
self.connect = connect
- self.paused = False
- self.holders = []
- self.pool = ConnectionPool(connect,
- maxConnections=2,
- dialect=self.dialect,
- paramstyle=self.paramstyle)
+ self.paused = False
+ self.holders = []
+ self.pool = ConnectionPool(
+ connect,
+ maxConnections=2,
+ dialect=self.dialect,
+ paramstyle=self.paramstyle
+ )
self.pool._createHolder = self.makeAHolder
- self.clock = self.pool.reactor = ClockWithThreads()
+ self.clock = self.pool.reactor = ClockWithThreads()
self.pool.startService()
test.addCleanup(self.flushHolders)
@@ -239,7 +240,7 @@
"""
Flush all pending C{submit}s since C{pauseHolders} was called. This
makes sure the service is stopped and the fake ThreadHolders are all
- executing their queues so failed tsets can exit cleanly.
+ executing their queues so failed tests can exit cleanly.
"""
self.paused = False
for holder in self.holders:
@@ -551,6 +552,21 @@
self._connectResultQueue.append(thunk)
+ def willConnectTo(self):
+ """
+ Queue a successful result for connect() and immediately add it as a
+ child to this L{ConnectionFactory}.
+
+ @return: a connection object
+ @rtype: L{FakeConnection}
+ """
+ aConnection = FakeConnection(self)
+ def thunk():
+ return aConnection
+ self._connectResultQueue.append(thunk)
+ return aConnection
+
+
def willFail(self):
"""
Used by tests to queue a successful result for connect().
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -115,7 +115,7 @@
(in the worst case) pass from worker->controller->controller->worker.
"""
- def performWork(table, workID):
+ def performWork(table, workID): #@NoSelf
"""
@param table: The table where work is waiting.
@type table: L{TableSyntax}
@@ -149,7 +149,8 @@
NodeTable.addColumn("PORT", SQLType("integer", None))
NodeTable.addColumn("TIME", SQLType("timestamp", None)).setDefaultValue(
# Note: in the real data structure, this is actually a not-cleaned-up
- # sqlparse internal data structure, but it *should* look closer to this.
+ # sqlparse internal data structure, but it *should* look closer to
+ # this.
ProcedureCall("timezone", ["UTC", NamedValue('CURRENT_TIMESTAMP')])
)
for column in NodeTable.columns:
@@ -370,7 +371,6 @@
will be taken care of by the job queueing machinery.
"""
-
@classmethod
def forTable(cls, table):
"""
@@ -677,8 +677,8 @@
"""
def __init__(self, peerPool, boxReceiver=None, locator=None):
- super(ConnectionFromWorker, self).__init__(peerPool.schema, boxReceiver,
- locator)
+ super(ConnectionFromWorker, self).__init__(peerPool.schema,
+ boxReceiver, locator)
self.peerPool = peerPool
self._load = 0
@@ -830,9 +830,9 @@
workItem = yield workItemClass.load(txn, workID)
if workItem.group is not None:
yield NamedLock.acquire(txn, workItem.group)
- # TODO: what if we fail? error-handling should be recorded someplace,
- # the row should probably be marked, re-tries should be triggerable
- # administratively.
+ # TODO: what if we fail? error-handling should be recorded
+ # someplace, the row should probably be marked, re-tries should be
+ # triggerable administratively.
yield workItem.delete()
# TODO: verify that workID is the primary key someplace.
yield workItem.doWork()
@@ -865,9 +865,6 @@
-
-
-
class WorkerFactory(Factory, object):
"""
Factory, to be used as the client to connect from the worker to the
@@ -950,7 +947,7 @@
waiting for the transaction where that addition was completed to
commit, and asking the local node controller process to do the work.
"""
- @passthru(self.workItemType.create(self.txn, **self.kw).addCallback)
+ created = self.workItemType.create(self.txn, **self.kw)
def whenCreated(item):
self._whenProposed.callback(self)
@self.txn.postCommit
@@ -967,12 +964,15 @@
self._whenExecuted.errback(why)
reactor = self._chooser.reactor
when = max(0, astimestamp(item.notBefore) - reactor.seconds())
- # TODO: Track the returned DelayedCall so it can be stopped when
- # the service stops.
+ # TODO: Track the returned DelayedCall so it can be stopped
+ # when the service stops.
self._chooser.reactor.callLater(when, maybeLater)
@self.txn.postAbort
def whenFailed():
self._whenCommitted.errback(TransactionFailed)
+ def whenNotCreated(failure):
+ self._whenProposed.errback(failure)
+ created.addCallbacks(whenCreated, whenNotCreated)
def whenExecuted(self):
@@ -1023,6 +1023,8 @@
"""
return _cloneDeferred(self._whenCommitted)
+
+
class _BaseQueuer(object):
implements(IQueuer)
@@ -1030,13 +1032,16 @@
super(_BaseQueuer, self).__init__()
self.proposalCallbacks = set()
+
def callWithNewProposals(self, callback):
- self.proposalCallbacks.add(callback);
+ self.proposalCallbacks.add(callback)
+
def transferProposalCallbacks(self, newQueuer):
newQueuer.proposalCallbacks = self.proposalCallbacks
return newQueuer
+
def enqueueWork(self, txn, workItemType, **kw):
"""
There is some work to do. Do it, someplace else, ideally in parallel.
@@ -1061,6 +1066,7 @@
return wp
+
class PeerConnectionPool(_BaseQueuer, MultiService, object):
"""
Each node has a L{PeerConnectionPool} connecting it to all the other nodes
@@ -1140,7 +1146,7 @@
self.mappedPeers = {}
self.schema = schema
self._startingUp = None
- self._listeningPortObject = None
+ self._listeningPort = None
self._lastSeenTotalNodes = 1
self._lastSeenNodeIndex = 1
@@ -1197,7 +1203,8 @@
A peer has requested us to perform some work; choose a work performer
local to this node, and then execute it.
"""
- return self.choosePerformer(onlyLocally=True).performWork(table, workID)
+ performer = self.choosePerformer(onlyLocally=True)
+ return performer.performWork(table, workID)
def allWorkItemTypes(self):
@@ -1225,8 +1232,8 @@
@return: the maximum number of other L{PeerConnectionPool} instances
that may be connected to the database described by
- C{self.transactionFactory}. Note that this is not the current count
- by connectivity, but the count according to the database.
+ C{self.transactionFactory}. Note that this is not the current
+ count by connectivity, but the count according to the database.
@rtype: L{int}
"""
# TODO
@@ -1277,7 +1284,6 @@
overdueItem.workID)
return inTransaction(self.transactionFactory, workCheck)
-
_currentWorkDeferred = None
_lostWorkCheckCall = None
@@ -1315,10 +1321,10 @@
@inlineCallbacks
def startup(txn):
endpoint = TCP4ServerEndpoint(self.reactor, self.ampPort)
- # If this fails, the failure mode is going to be ugly, just like all
- # conflicted-port failures. But, at least it won't proceed.
- self._listeningPortObject = yield endpoint.listen(self.peerFactory())
- self.ampPort = self._listeningPortObject.getHost().port
+ # If this fails, the failure mode is going to be ugly, just like
+ # all conflicted-port failures. But, at least it won't proceed.
+ self._listeningPort = yield endpoint.listen(self.peerFactory())
+ self.ampPort = self._listeningPort.getHost().port
yield Lock.exclusive(NodeInfo.table).on(txn)
nodes = yield self.activeNodes(txn)
selves = [node for node in nodes
@@ -1354,8 +1360,8 @@
yield super(PeerConnectionPool, self).stopService()
if self._startingUp is not None:
yield self._startingUp
- if self._listeningPortObject is not None:
- yield self._listeningPortObject.stopListening()
+ if self._listeningPort is not None:
+ yield self._listeningPort.stopListening()
if self._lostWorkCheckCall is not None:
self._lostWorkCheckCall.cancel()
if self._currentWorkDeferred is not None:
@@ -1430,8 +1436,6 @@
-
-
class LocalQueuer(_BaseQueuer):
"""
When work is enqueued with this queuer, it is just executed locally.
@@ -1458,7 +1462,8 @@
"""
Implementor of C{performWork} that doesn't actual perform any work. This
is used in the case where you want to be able to enqueue work for someone
- else to do, but not take on any work yourself (such as a command line tool).
+ else to do, but not take on any work yourself (such as a command line
+ tool).
"""
implements(_IWorkPerformer)
@@ -1469,6 +1474,7 @@
return succeed(None)
+
class NonPerformingQueuer(_BaseQueuer):
"""
When work is enqueued with this queuer, it is never executed locally.
@@ -1487,4 +1493,4 @@
"""
Choose to perform the work locally.
"""
- return NonPerformer()
\ No newline at end of file
+ return NonPerformer()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -36,6 +36,7 @@
)
from twisted.trial.unittest import TestCase
+from twisted.python.failure import Failure
from twisted.internet.defer import (
Deferred, inlineCallbacks, gatherResults, passthru#, returnValue
)
@@ -55,6 +56,8 @@
from twisted.test.proto_helpers import StringTransport, MemoryReactor
from twext.enterprise.fixtures import SteppablePoolHelper
from twisted.internet.defer import returnValue
+from twext.enterprise.queue import LocalQueuer
+from twext.enterprise.fixtures import ConnectionPoolHelper
from twext.enterprise.queue import _BaseQueuer, NonPerformingQueuer
import twext.enterprise.queue
@@ -67,7 +70,7 @@
def callLater(self, _seconds, _f, *args, **kw):
if _seconds < 0:
- raise ValueError("%s<0: "%(_seconds,))
+ raise ValueError("%s<0: " % (_seconds,))
return super(Clock, self).callLater(_seconds, _f, *args, **kw)
@@ -267,6 +270,56 @@
+class WorkProposalTests(TestCase):
+ """
+ Tests for L{WorkProposal}.
+ """
+
+ def test_whenProposedSuccess(self):
+ """
+ The L{Deferred} returned by L{WorkProposal.whenProposed} fires when the
+ SQL sent to the database has completed.
+ """
+ cph = ConnectionPoolHelper()
+ cph.setUp(test=self)
+ cph.pauseHolders()
+ lq = LocalQueuer(cph.createTransaction)
+ enqTxn = cph.createTransaction()
+ wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+ d = wp.whenProposed()
+ r = cph.resultOf(d)
+ self.assertEquals(r, [])
+ cph.flushHolders()
+ self.assertEquals(len(r), 1)
+
+
+ def test_whenProposedFailure(self):
+ """
+ The L{Deferred} returned by L{WorkProposal.whenProposed} fails with an
+ errback when the SQL executed to create the WorkItem row fails.
+ """
+ cph = ConnectionPoolHelper()
+ cph.setUp(self)
+ cph.pauseHolders()
+ firstConnection = cph.factory.willConnectTo()
+ enqTxn = cph.createTransaction()
+ # Execute some SQL on the connection before enqueueing the work-item so
+ # that we don't get the initial-statement.
+ enqTxn.execSQL("some sql")
+ lq = LocalQueuer(cph.createTransaction)
+ cph.flushHolders()
+ cph.pauseHolders()
+ wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+ firstConnection.executeWillFail(lambda: RuntimeError("foo"))
+ d = wp.whenProposed()
+ r = cph.resultOf(d)
+ self.assertEquals(r, [])
+ cph.flushHolders()
+ self.assertEquals(len(r), 1)
+ self.assertIsInstance(r[0], Failure)
+
+
+
class PeerConnectionPoolUnitTests(TestCase):
"""
L{PeerConnectionPool} has many internal components.
@@ -393,7 +446,8 @@
# Next, create one that's actually far enough into the past to run.
yield DummyWorkItem.create(
txn, a=3, b=4, notBefore=(
- # Schedule it in the past so that it should have already run.
+ # Schedule it in the past so that it should have already
+ # run.
fakeNow - datetime.timedelta(
seconds=qpool.queueProcessTimeout + 20
)
@@ -509,8 +563,8 @@
t = StringTransport()
p.makeConnection(t)
return p, t
- worker1, trans1 = peer()
- worker2, trans2 = peer()
+ worker1, _ignore_trans1 = peer()
+ worker2, _ignore_trans2 = peer()
# Ask the worker to do something.
worker1.performWork(schema.DUMMY_WORK_ITEM, 1)
self.assertEquals(worker1.currentLoad, 1)
@@ -619,11 +673,12 @@
self.receiver, self.sender = self.sender, self.receiver
return result
+
def flush(self, turns=10):
"""
Keep relaying data until there's no more.
"""
- for x in range(turns):
+ for _ignore_x in range(turns):
if not (self.pump() or self.pump()):
return
@@ -718,7 +773,7 @@
def op2(txn):
return Select([schema.DUMMY_WORK_DONE.WORK_ID,
schema.DUMMY_WORK_DONE.A_PLUS_B],
- From=schema.DUMMY_WORK_DONE).on(txn)
+ From=schema.DUMMY_WORK_DONE).on(txn)
rows = yield inTransaction(self.store.newTransaction, op2)
self.assertEquals(rows, [[4321, 7]])
@@ -729,7 +784,7 @@
When a L{WorkItem} is concurrently deleted by another transaction, it
should I{not} perform its work.
"""
- # Provide access to a method called 'concurrently' everything using
+ # Provide access to a method called 'concurrently' everything using
original = self.store.newTransaction
def decorate(*a, **k):
result = original(*a, **k)
@@ -746,13 +801,13 @@
# Sanity check on the concurrent deletion.
def op2(txn):
return Select([schema.DUMMY_WORK_ITEM.WORK_ID],
- From=schema.DUMMY_WORK_ITEM).on(txn)
+ From=schema.DUMMY_WORK_ITEM).on(txn)
rows = yield inTransaction(self.store.newTransaction, op2)
self.assertEquals(rows, [])
def op3(txn):
return Select([schema.DUMMY_WORK_DONE.WORK_ID,
schema.DUMMY_WORK_DONE.A_PLUS_B],
- From=schema.DUMMY_WORK_DONE).on(txn)
+ From=schema.DUMMY_WORK_DONE).on(txn)
rows = yield inTransaction(self.store.newTransaction, op3)
self.assertEquals(rows, [])
@@ -763,18 +818,23 @@
def __init__(self, *ignored):
pass
+
def _start(self):
pass
+
+
class BaseQueuerTests(TestCase):
def setUp(self):
self.proposal = None
self.patch(twext.enterprise.queue, "WorkProposal", DummyProposal)
+
def _proposalCallback(self, proposal):
self.proposal = proposal
+
def test_proposalCallbacks(self):
queuer = _BaseQueuer()
queuer.callWithNewProposals(self._proposalCallback)
@@ -783,6 +843,7 @@
self.assertNotEqual(self.proposal, None)
+
class NonPerformingQueuerTests(TestCase):
@inlineCallbacks
@@ -791,5 +852,3 @@
performer = queuer.choosePerformer()
result = (yield performer.performWork(None, None))
self.assertEquals(result, None)
-
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -94,8 +94,8 @@
A socket in the master process pointing at a file descriptor that can be
used to transmit sockets to a subprocess.
- @ivar skt: the UNIX socket used as the sendmsg() transport.
- @type skt: L{socket.socket}
+ @ivar outSocket: the UNIX socket used as the sendmsg() transport.
+ @type outSocket: L{socket.socket}
@ivar outgoingSocketQueue: an outgoing queue of sockets to send to the
subprocess, along with their descriptions (strings describing their
@@ -115,12 +115,13 @@
@type dispatcher: L{InheritedSocketDispatcher}
"""
- def __init__(self, dispatcher, skt, status):
+ def __init__(self, dispatcher, inSocket, outSocket, status):
FileDescriptor.__init__(self, dispatcher.reactor)
self.status = status
self.dispatcher = dispatcher
- self.skt = skt # XXX needs to be set non-blocking by somebody
- self.fileno = skt.fileno
+ self.inSocket = inSocket
+ self.outSocket = outSocket # XXX needs to be set non-blocking by somebody
+ self.fileno = outSocket.fileno
self.outgoingSocketQueue = []
self.pendingCloseSocketQueue = []
@@ -138,7 +139,7 @@
Receive a status / health message and record it.
"""
try:
- data, _ignore_flags, _ignore_ancillary = recvmsg(self.skt.fileno())
+ data, _ignore_flags, _ignore_ancillary = recvmsg(self.outSocket.fileno())
except SocketError, se:
if se.errno not in (EAGAIN, ENOBUFS):
raise
@@ -155,7 +156,7 @@
while self.outgoingSocketQueue:
skt, desc = self.outgoingSocketQueue.pop(0)
try:
- sendfd(self.skt.fileno(), skt.fileno(), desc)
+ sendfd(self.outSocket.fileno(), skt.fileno(), desc)
except SocketError, se:
if se.errno in (EAGAIN, ENOBUFS):
self.outgoingSocketQueue.insert(0, (skt, desc))
@@ -341,14 +342,27 @@
i, o = socketpair()
i.setblocking(False)
o.setblocking(False)
- a = _SubprocessSocket(self, o, self.statusWatcher.initialStatus())
+ a = _SubprocessSocket(self, i, o, self.statusWatcher.initialStatus())
self._subprocessSockets.append(a)
if self._isDispatching:
a.startReading()
return i
+ def removeSocket(self, skt):
+ """
+ Removes a previously added socket from the pool of sockets being used
+ for transmitting file descriptors to child processes.
+ """
+ for a in self._subprocessSockets:
+ if a.inSocket == skt:
+ self._subprocessSockets.remove(a)
+ break
+ else:
+ raise ValueError("Unknown socket: {0}".format(skt))
+
+
class InheritedPort(FileDescriptor, object):
"""
An L{InheritedPort} is an L{IReadDescriptor}/L{IWriteDescriptor} created in
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py (from rev 12016, CalendarServer/trunk/twext/protocols/echo.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,35 @@
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Echo protocol.
+"""
+
+__all__ = ["EchoProtocol"]
+
+from twisted.internet.protocol import Protocol
+
+
+class EchoProtocol(Protocol):
+ """
+ Say what you hear.
+ """
+
+ def dataReceived(self, data):
+ """
+ As soon as any data is received, write it back.
+ """
+ self.transport.write(data)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,7 +14,17 @@
from twisted.internet.defer import Deferred, gatherResults, TimeoutError
+def onConnectionLossFire(protocol, deferred):
+ """
+ When the given L{MemCacheProtocol} is disconnected, fire the given
+ L{Deferred} with L{None}.
+ """
+ def cl(reason):
+ deferred.callback(None)
+ protocol.connectionLost = cl
+
+
class MemCacheTestCase(TestCase):
"""
Test client protocol class L{MemCacheProtocol}.
@@ -244,7 +254,7 @@
d1 = self.proto.get("foo")
d2 = self.proto.get("bar")
d3 = Deferred()
- self.proto.connectionLost = d3.callback
+ onConnectionLossFire(self.proto, d3)
self.clock.advance(self.proto.persistentTimeOut)
self.assertFailure(d1, TimeoutError)
@@ -280,7 +290,7 @@
"""
d1 = self.proto.get("foo")
d2 = Deferred()
- self.proto.connectionLost = d2.callback
+ onConnectionLossFire(self.proto, d2)
self.proto.dataReceived("VALUE foo 0 10\r\n12345")
self.clock.advance(self.proto.persistentTimeOut)
@@ -295,7 +305,7 @@
"""
d1 = self.proto.stats()
d2 = Deferred()
- self.proto.connectionLost = d2.callback
+ onConnectionLossFire(self.proto, d2)
self.proto.dataReceived("STAT foo bar\r\n")
self.clock.advance(self.proto.persistentTimeOut)
@@ -311,7 +321,7 @@
d1 = self.proto.get("foo")
d2 = self.proto.get("bar")
d3 = Deferred()
- self.proto.connectionLost = d3.callback
+ onConnectionLossFire(self.proto, d3)
self.clock.advance(self.proto.persistentTimeOut - 1)
self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
@@ -319,7 +329,7 @@
def check(result):
self.assertEquals(result, (0, "bar"))
self.assertEquals(len(self.clock.calls), 1)
- for i in range(self.proto.persistentTimeOut):
+ for _ignore_i in range(self.proto.persistentTimeOut):
self.clock.advance(1)
return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
def checkTime(ignored):
@@ -338,7 +348,7 @@
"""
d1 = self.proto.get("foo")
d3 = Deferred()
- self.proto.connectionLost = d3.callback
+ onConnectionLossFire(self.proto, d3)
self.clock.advance(self.proto.persistentTimeOut - 1)
d2 = self.proto.get("bar")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,8 +30,8 @@
from twext.who.idirectory import DirectoryConfigurationError
from twext.who.idirectory import IDirectoryService
-from twext.who.index import DirectoryService as BaseDirectoryService
-from twext.who.index import DirectoryRecord
+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.directory import DirectoryRecord
from twext.who.util import ConstantsContainer
@@ -78,10 +78,10 @@
return self._recordType
- def recordsFromExpression(self, expression, records=None):
+ def recordsFromExpression(self, expression):
ds = []
for service in self.services:
- d = service.recordsFromExpression(expression, records)
+ d = service.recordsFromExpression(expression)
ds.append(d)
def unwrapFirstError(f):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,35 +24,81 @@
"DirectoryRecord",
]
-from uuid import UUID
+from zope.interface import implementer
-from zope.interface import implements
-
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.defer import succeed, fail
from twext.who.idirectory import QueryNotSupportedError, NotAllowedError
from twext.who.idirectory import FieldName, RecordType
-from twext.who.idirectory import Operand
from twext.who.idirectory import IDirectoryService, IDirectoryRecord
+from twext.who.expression import CompoundExpression, Operand
from twext.who.expression import MatchExpression
from twext.who.util import uniqueResult, describe
+ at implementer(IDirectoryService)
class DirectoryService(object):
- implements(IDirectoryService)
+ """
+ Generic (and abstract) implementation of L{IDirectoryService}.
+ Most of the C{recordsWith*} methods call L{recordsWithFieldValue}, which in
+ turn calls L{recordsFromExpression} with a corresponding
+ L{MatchExpression}.
+
+ L{recordsFromExpression} relies on L{recordsFromNonCompoundExpression} for
+ all expression types other than L{CompoundExpression}, which it handles
+ directly.
+
+ L{recordsFromNonCompoundExpression} (and therefore most uses of the other
+ methods) will always fail with a L{QueryNotSupportedError}.
+
+ A subclass should therefore override L{recordsFromNonCompoundExpression}
+ with an implementation that handles any queries that it can support (which
+ should include L{MatchExpression}) and calls its superclass' implementation
+ with any query it cannot support.
+
+ A subclass may override L{recordsFromExpression} if it is to support
+ L{CompoundExpression}s with operands other than L{Operand.AND} and
+ L{Operand.OR}.
+
+ A subclass may override L{recordsFromExpression} if it is built on top
+ of a directory service that supports compound expressions, as that may be
+ more effient than relying on L{DirectoryService}'s implementation.
+
+ L{updateRecords} and L{removeRecords} will fail with L{NotAllowedError}
+ when asked to modify data.
+ A subclass should override these methods if is to allow editing of
+ directory information.
+
+ @cvar recordType: a L{Names} class or compatible object (eg.
+ L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+ the record types that are supported by this directory service.
+
+ @cvar fieldName: a L{Names} class or compatible object (eg.
+ L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+ the record field names that are supported by this directory service.
+
+ @cvar normalizedFields: a L{dict} mapping of (ie. L{NamedConstant}s
+ contained in the C{fieldName} class variable) to callables that take
+ a field value (a L{unicode}) and return a normalized field value (also
+ a L{unicode}).
+ """
+
recordType = RecordType
fieldName = FieldName
normalizedFields = {
- FieldName.guid: lambda g: UUID(g).hex,
- FieldName.emailAddresses: lambda e: e.lower(),
+ FieldName.emailAddresses: lambda e: bytes(e).lower(),
}
def __init__(self, realmName):
+ """
+ @param realmName: a realm name
+ @type realmName: L{unicode}
+ """
self.realmName = realmName
@@ -67,59 +113,98 @@
return self.recordType.iterconstants()
- def recordsFromExpression(self, expression, records=None):
+ def recordsFromNonCompoundExpression(self, expression, records=None):
"""
- Finds records matching a single expression.
- @param expression: an expression
+ Finds records matching a expression.
+
+ @note: This method is called by L{recordsFromExpression} to handle
+ all expressions other than L{CompoundExpression}.
+ This implementation always fails with L{QueryNotSupportedError}.
+ Subclasses should override this in order to handle additional
+ expression types, and call on the superclass' implementation
+ for other expression types.
+
+ @note: This interface is the same as L{recordsFromExpression}, except
+ for the additional C{records} argument.
+
+ @param expression: an expression to apply
@type expression: L{object}
- @param records: a set of records to search within. C{None} if
+
+ @param records: a set of records to limit the search to. C{None} if
the whole directory should be searched.
+ This is provided by L{recordsFromExpression} when it has already
+ narrowed down results to a set of records.
+ That is, it's a performance optimization; ignoring this and
+ searching the entire directory will also work.
@type records: L{set} or L{frozenset}
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
+
+ @raises: L{QueryNotSupportedError} if the expression is not
+ supported by this directory service.
"""
+ if records is not None:
+ for record in records:
+ break
+ else:
+ return succeed(())
+
return fail(QueryNotSupportedError(
"Unknown expression: {0}".format(expression)
))
@inlineCallbacks
- def recordsFromQuery(self, expressions, operand=Operand.AND):
- expressionIterator = iter(expressions)
+ def recordsFromExpression(self, expression):
+ if isinstance(expression, CompoundExpression):
+ operand = expression.operand
+ subExpressions = iter(expression.expressions)
- try:
- expression = expressionIterator.next()
- except StopIteration:
- returnValue(())
+ try:
+ subExpression = subExpressions.next()
+ except StopIteration:
+ returnValue(())
- results = set((yield self.recordsFromExpression(expression)))
+ results = set((
+ yield self.recordsFromNonCompoundExpression(subExpression)
+ ))
- for expression in expressions:
- if operand == Operand.AND:
- if not results:
- # No need to bother continuing here
- returnValue(())
+ for subExpression in subExpressions:
+ if operand == Operand.AND:
+ if not results:
+ # No need to bother continuing here
+ returnValue(())
- records = results
- else:
- records = None
+ records = results
+ else:
+ records = None
- recordsMatchingExpression = frozenset((
- yield self.recordsFromExpression(expression, records=records)
- ))
+ recordsMatchingExpression = frozenset((
+ yield self.recordsFromNonCompoundExpression(
+ subExpression,
+ records=records
+ )
+ ))
- if operand == Operand.AND:
- results &= recordsMatchingExpression
- elif operand == Operand.OR:
- results |= recordsMatchingExpression
- else:
- raise QueryNotSupportedError(
- "Unknown operand: {0}".format(operand)
- )
+ if operand == Operand.AND:
+ results &= recordsMatchingExpression
+ elif operand == Operand.OR:
+ results |= recordsMatchingExpression
+ else:
+ raise QueryNotSupportedError(
+ "Unknown operand: {0}".format(operand)
+ )
+ else:
+ results = yield self.recordsFromNonCompoundExpression(expression)
returnValue(results)
def recordsWithFieldValue(self, fieldName, value):
- return self.recordsFromExpression(MatchExpression(fieldName, value))
+ return self.recordsFromExpression(
+ MatchExpression(fieldName, value)
+ )
@inlineCallbacks
@@ -142,10 +227,17 @@
@inlineCallbacks
def recordWithShortName(self, recordType, shortName):
- returnValue(uniqueResult((yield self.recordsFromQuery((
- MatchExpression(FieldName.recordType, recordType),
- MatchExpression(FieldName.shortNames, shortName),
- )))))
+ returnValue(uniqueResult((
+ yield self.recordsFromExpression(
+ CompoundExpression(
+ (
+ MatchExpression(FieldName.recordType, recordType),
+ MatchExpression(FieldName.shortNames, shortName),
+ ),
+ operand=Operand.AND
+ )
+ )
+ )))
def recordsWithEmailAddress(self, emailAddress):
@@ -158,17 +250,32 @@
def updateRecords(self, records, create=False):
for record in records:
return fail(NotAllowedError("Record updates not allowed."))
+ return succeed(None)
def removeRecords(self, uids):
for uid in uids:
return fail(NotAllowedError("Record removal not allowed."))
+ return succeed(None)
+ at implementer(IDirectoryRecord)
class DirectoryRecord(object):
- implements(IDirectoryRecord)
+ """
+ Generic implementation of L{IDirectoryService}.
+ This is an incomplete implementation of L{IDirectoryRecord}.
+
+ L{groups} will always fail with L{NotImplementedError} and L{members} will
+ do so if this is a group record.
+ A subclass should override these methods to support group membership and
+ complete this implementation.
+
+ @cvar requiredFields: an iterable of field names that must be present in
+ all directory records.
+ """
+
requiredFields = (
FieldName.uid,
FieldName.recordType,
@@ -183,11 +290,6 @@
if FieldName.isMultiValue(fieldName):
values = fields[fieldName]
- if len(values) == 0:
- raise ValueError(
- "{0} field must have at least one value."
- .format(fieldName)
- )
for value in values:
if not value:
raise ValueError(
@@ -262,32 +364,47 @@
def description(self):
- description = [self.__class__.__name__, ":"]
+ """
+ Generate a string description of this directory record.
- for name, value in self.fields.items():
+ @return: A description.
+ @rtype: L{unicode}
+ """
+ description = [self.__class__.__name__, u":"]
+
+ for name in sorted(self.service.fieldName.iterconstants()):
+ if name not in self.fields:
+ continue
+
+ value = self.fields[name]
+
if hasattr(name, "description"):
name = name.description
else:
- name = str(name)
+ name = unicode(name)
if hasattr(value, "description"):
value = value.description
else:
- value = str(value)
+ value = unicode(value)
- description.append("\n ")
+ description.append(u"\n ")
description.append(name)
- description.append(" = ")
+ description.append(u" = ")
description.append(value)
- return "".join(description)
+ description.append(u"\n")
+ return u"".join(description)
+
def members(self):
if self.recordType == RecordType.group:
- raise NotImplementedError("Subclasses must implement members()")
+ return fail(
+ NotImplementedError("Subclasses must implement members()")
+ )
return succeed(())
def groups(self):
- raise NotImplementedError("Subclasses must implement groups()")
+ return fail(NotImplementedError("Subclasses must implement groups()"))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,6 +20,9 @@
"""
__all__ = [
+ "Operand",
+ "CompoundExpression",
+
"MatchType",
"MatchFlags",
"MatchExpression",
@@ -28,25 +31,55 @@
from twisted.python.constants import Names, NamedConstant
from twisted.python.constants import Flags, FlagConstant
+from twext.who.util import iterFlags, describe
-##
-# Match expression
-##
+#
+# Compound expression
+#
+class Operand(Names):
+ """
+ Contants for common operands.
+ """
+ OR = NamedConstant()
+ AND = NamedConstant()
+ OR.description = u"or"
+ AND.description = u"and"
+
+
+class CompoundExpression(object):
+ """
+ An expression that groups multiple expressions with an operand.
+
+ @ivar expressions: An iterable of expressions.
+
+ @ivar operand: A L{NamedConstant} specifying an operand.
+ """
+
+ def __init__(self, expressions, operand):
+ self.expressions = expressions
+ self.operand = operand
+
+
+#
+# Match expression
+#
+
class MatchType(Names):
"""
Query match types.
"""
- equals = NamedConstant()
+ equals = NamedConstant()
+ equals.description = u"equals"
+
startsWith = NamedConstant()
- contains = NamedConstant()
+ startsWith.description = u"starts with"
- equals.description = "equals"
- startsWith.description = "starts with"
- contains.description = "contains"
+ contains = NamedConstant()
+ contains.description = u"contains"
@@ -55,21 +88,95 @@
Match expression flags.
"""
NOT = FlagConstant()
- NOT.description = "not"
+ NOT.description = u"not"
caseInsensitive = FlagConstant()
- caseInsensitive.description = "case insensitive"
+ caseInsensitive.description = u"case insensitive"
+ @staticmethod
+ def _setMatchFunctions(flags):
+ """
+ Compute a predicate and normalize functions for the given match
+ expression flags.
+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: Predicate and normalize functions.
+ @rtype: L{tuple} of callables.
+ """
+ predicate = lambda x: x
+ normalize = lambda x: x
+
+ if flags is None:
+ flags = FlagConstant()
+ else:
+ for flag in iterFlags(flags):
+ if flag == MatchFlags.NOT:
+ predicate = lambda x: not x
+ elif flag == MatchFlags.caseInsensitive:
+ normalize = lambda x: x.lower()
+ else:
+ raise NotImplementedError(
+ "Unknown query flag: {0}".format(describe(flag))
+ )
+
+ flags._predicate = predicate
+ flags._normalize = normalize
+
+ return flags
+
+
+ @staticmethod
+ def predicator(flags):
+ """
+ Determine a predicate function for the given flags.
+
+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: a L{callable} that accepts an L{object} argument and returns a
+ L{object} that has the opposite or same truth value as the argument,
+ depending on whether L{MatchFlags.NOT} is or is not in C{flags}.
+ @rtype: callable
+ """
+ if not hasattr(flags, "_predicate"):
+ flags = MatchFlags._setMatchFunctions(flags)
+ return flags._predicate
+
+
+ @staticmethod
+ def normalizer(flags):
+ """
+ Determine a predicate function for the given flags.
+
+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: a L{callable} that accepts a L{unicode} and returns the same
+ L{unicode} or a normalized L{unicode} that can be compared with other
+ normalized L{unicode}s in a case-insensitive fashion, depending on
+ whether L{MatchFlags.caseInsensitive} is not or is in C{flags}.
+ @rtype: callable
+ """
+ if not hasattr(flags, "_normalize"):
+ flags = MatchFlags._setMatchFunctions(flags)
+ return flags._normalize
+
+
+
class MatchExpression(object):
"""
Query for a matching value in a given field.
- @ivar fieldName: a L{NamedConstant} specifying the field
- @ivar fieldValue: a text value to match
- @ivar matchType: a L{NamedConstant} specifying the match algorythm
- @ivar flags: L{NamedConstant} specifying additional options
+ @ivar fieldName: A L{NamedConstant} specifying the field.
+
+ @ivar fieldValue: A value to match.
+
+ @ivar matchType: A L{NamedConstant} specifying the match algorithm.
+
+ @ivar flags: A L{NamedConstant} specifying additional options.
"""
def __init__(
@@ -77,14 +184,15 @@
fieldName, fieldValue,
matchType=MatchType.equals, flags=None
):
- self.fieldName = fieldName
+ self.fieldName = fieldName
self.fieldValue = fieldValue
- self.matchType = matchType
- self.flags = flags
+ self.matchType = matchType
+ self.flags = flags
+
def __repr__(self):
def describe(constant):
- return getattr(constant, "description", str(constant))
+ return getattr(constant, "description", unicode(constant))
if self.flags is None:
flags = ""
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
##
"""
-Directory service interface.
+Directory service interfaces.
"""
__all__ = [
@@ -30,21 +30,22 @@
"RecordType",
"FieldName",
- "Operand",
"IDirectoryService",
"IDirectoryRecord",
]
+from uuid import UUID
+
from zope.interface import Attribute, Interface
from twisted.python.constants import Names, NamedConstant
-##
+#
# Exceptions
-##
+#
class DirectoryServiceError(Exception):
"""
@@ -55,7 +56,7 @@
class DirectoryConfigurationError(DirectoryServiceError):
"""
- Directory configurtion error.
+ Directory configuration error.
"""
@@ -93,27 +94,54 @@
class NotAllowedError(DirectoryServiceError):
"""
- Apparently, you can't do that.
+ It seems you aren't permitted to do that.
"""
-##
+#
# Data Types
-##
+#
class RecordType(Names):
+ """
+ Constants for common directory record types.
+ """
user = NamedConstant()
group = NamedConstant()
- user.description = "user"
- group.description = "group"
+ user.description = u"user"
+ group.description = u"group"
class FieldName(Names):
"""
- Constants for common field names.
+ Constants for common directory record field names.
+
+ Fields as assciated with either a single value or an iterable of values.
+
+ @cvar uid: The primary unique identifier for a directory record.
+ The associated value must be a L{unicode}.
+
+ @cvar guid: The globally unique identifier for a directory record.
+ The associated value must be a L{UUID} or C{None}.
+
+ @cvar recordType: The type of a directory record.
+ The associated value must be a L{NamedConstant}.
+
+ @cvar shortNames: The short names for a directory record.
+ The associated values must L{unicode}s and there must be at least
+ one associated value.
+
+ @cvar fullNames: The full names for a directory record.
+ The associated values must be L{unicode}s.
+
+ @cvar emailAddresses: The email addresses for a directory record.
+ The associated values must be L{unicodes}.
+
+ @cvar password: The clear text password for a directory record.
+ The associated value must be a L{unicode} or C{None}.
"""
uid = NamedConstant()
guid = NamedConstant()
@@ -123,14 +151,16 @@
emailAddresses = NamedConstant()
password = NamedConstant()
- uid.description = "UID"
- guid.description = "GUID"
- recordType.description = "record type"
- shortNames.description = "short names"
- fullNames.description = "full names"
- emailAddresses.description = "email addresses"
- password.description = "password"
+ uid.description = u"UID"
+ guid.description = u"GUID"
+ recordType.description = u"record type"
+ shortNames.description = u"short names"
+ fullNames.description = u"full names"
+ emailAddresses.description = u"email addresses"
+ password.description = u"password"
+ guid.valueType = UUID
+
shortNames.multiValue = True
fullNames.multiValue = True
emailAddresses.multiValue = True
@@ -138,22 +168,36 @@
@staticmethod
def isMultiValue(name):
+ """
+ Check for whether a field is multi-value (as opposed to single-value).
+
+ @param name: The name of the field.
+ @type name: L{NamedConstant}
+
+ @return: C{True} if the field is multi-value, C{False} otherwise.
+ @rtype: L{BOOL}
+ """
return getattr(name, "multiValue", False)
+ @staticmethod
+ def valueType(name):
+ """
+ Check for the expected type of values for a field.
-class Operand(Names):
- OR = NamedConstant()
- AND = NamedConstant()
+ @param name: The name of the field.
+ @type name: L{NamedConstant}
- OR.description = "or"
- AND.description = "and"
+ @return: The expected type.
+ @rtype: L{type}
+ """
+ return getattr(name, "valueType", unicode)
-##
+#
# Interfaces
-##
+#
class IDirectoryService(Interface):
"""
@@ -169,7 +213,15 @@
A directory service may allow support the editing, removal and
addition of records.
+ Services are read-only should fail with L{NotAllowedError} in editing
+ methods.
+
+ The L{FieldName.uid} field, the L{FieldName.guid} field (if not C{None}),
+ and the combination of the L{FieldName.recordType} and
+ L{FieldName.shortName} fields must be unique to each directory record
+ vended by a directory service.
"""
+
realmName = Attribute(
"The name of the authentication realm this service represents."
)
@@ -177,32 +229,24 @@
def recordTypes():
"""
- @return: an iterable of L{NamedConstant}s denoting the record
- types that are kept in this directory.
+ Get the record types supported by this directory service.
+
+ @return: The record types that are supported by this directory service.
+ @rtype: iterable of L{NamedConstant}s
"""
- def recordsFromExpression(self, expression):
+ def recordsFromExpression(expression):
"""
Find records matching an expression.
+
@param expression: an expression to apply
@type expression: L{object}
- @return: a deferred iterable of matching L{IDirectoryRecord}s.
- @raises: L{QueryNotSupportedError} if the expression is not
- supported by this directory service.
- """
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
- def recordsFromQuery(expressions, operand=Operand.AND):
- """
- Find records by composing a query consisting of an iterable of
- expressions and an operand.
- @param expressions: expressions to query against
- @type expressions: iterable of L{object}s
- @param operand: an operand
- @type operand: a L{NamedConstant}
- @return: a deferred iterable of matching L{IDirectoryRecord}s.
- @raises: L{QueryNotSupportedError} if the query is not
+ @raises: L{QueryNotSupportedError} if the expression is not
supported by this directory service.
"""
@@ -211,80 +255,111 @@
"""
Find records that have the given field name with the given
value.
+
@param fieldName: a field name
@type fieldName: L{NamedConstant}
+
@param value: a value to match
@type value: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s.
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
"""
def recordWithUID(uid):
"""
Find the record that has the given UID.
+
@param uid: a UID
@type uid: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
"""
def recordWithGUID(guid):
"""
Find the record that has the given GUID.
+
@param guid: a GUID
- @type guid: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
+ @type guid: L{UUID}
+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
"""
def recordsWithRecordType(recordType):
"""
Find the records that have the given record type.
+
@param recordType: a record type
@type recordType: L{NamedConstant}
- @return: a deferred iterable of L{IDirectoryRecord}s.
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
"""
def recordWithShortName(recordType, shortName):
"""
Find the record that has the given record type and short name.
+
@param recordType: a record type
@type recordType: L{NamedConstant}
+
@param shortName: a short name
@type shortName: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
"""
def recordsWithEmailAddress(emailAddress):
"""
Find the records that have the given email address.
+
@param emailAddress: an email address
@type emailAddress: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
"""
def updateRecords(records, create=False):
"""
Updates existing directory records.
+
@param records: the records to update
@type records: iterable of L{IDirectoryRecord}s
+
@param create: if true, create records if necessary
@type create: boolean
+
+ @return: unspecifiied
+ @rtype: deferred object
+
+ @raises L{NotAllowedError}: if the update is not allowed by the
+ directory service.
"""
def removeRecords(uids):
"""
Removes the records with the given UIDs.
+
@param uids: the UIDs of the records to remove
@type uids: iterable of L{bytes}
+
+ @return: unspecifiied
+ @rtype: deferred object
+
+ @raises L{NotAllowedError}: if the removal is not allowed by the
+ directory service.
"""
@@ -325,6 +400,7 @@
"""
Find the records that are members of this group. Only direct
members are included; members of members are not expanded.
+
@return: a deferred iterable of L{IDirectoryRecord}s which are
direct members of this group.
"""
@@ -335,6 +411,7 @@
Find the group records that this record is a member of. Only
groups for which this record is a direct member is are
included; membership is not expanded.
+
@return: a deferred iterable of L{IDirectoryRecord}s which are
groups that this record is a member of.
"""
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,7 +30,7 @@
from twisted.internet.defer import succeed, inlineCallbacks, returnValue
from twext.who.util import ConstantsContainer
-from twext.who.util import describe, uniqueResult, iterFlags
+from twext.who.util import uniqueResult
from twext.who.idirectory import FieldName as BaseFieldName
from twext.who.expression import MatchExpression, MatchType, MatchFlags
from twext.who.directory import DirectoryService as BaseDirectoryService
@@ -44,7 +44,7 @@
class FieldName(Names):
memberUIDs = NamedConstant()
- memberUIDs.description = "member UIDs"
+ memberUIDs.description = u"member UIDs"
memberUIDs.multiValue = True
@@ -55,7 +55,91 @@
class DirectoryService(BaseDirectoryService):
"""
- XML directory service.
+ Generic (and abstract) in-memory-indexed directory service.
+
+ This class implements the record access API in L{BaseDirectoryService} by
+ caching all records in an in-memory dictionary.
+
+ Each indexed field has a top-level key in the index and in turn contains
+ a dictionary in which keys are field values, and values are directory
+ records which have a matching field value for the cooresponding key::
+
+ {
+ <FieldName1>: {
+ <value1a>: set([<record1a1>, ...]),
+ ...
+ },
+ ...
+ }
+
+ Here is an example index for a service with a three user records and one
+ group record::
+
+ {
+ <FieldName=uid>: {
+ u'__calendar-dev__': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ u'__dre__': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ u'__sagen__': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ u'__wsanchez__': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=recordType>: {
+ <RecordType=group>: set([
+ <DirectoryRecord (group)calendar-dev>,
+ ]),
+ <RecordType=user>: set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=shortNames>: {
+ u'calendar-dev': set([<DirectoryRecord (group)calendar-dev>]),
+ u'dre': set([<DirectoryRecord (user)dre>]),
+ u'sagen': set([<DirectoryRecord (user)sagen>]),
+ u'wilfredo_sanchez': set([<DirectoryRecord (user)wsanchez>]),
+ u'wsanchez': set([<DirectoryRecord (user)wsanchez>])
+ },
+ <FieldName=emailAddresses>: {
+ 'dev at bitbucket.calendarserver.org': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ 'dre at bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ 'sagen at bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ 'shared at example.com': set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)dre>
+ ]),
+ 'wsanchez at bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)wsanchez>
+ ]),
+ 'wsanchez at devnull.twistedmatrix.com': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=memberUIDs>: {
+ u'__sagen__': set([<DirectoryRecord (group)calendar-dev>]),
+ u'__wsanchez__': set([<DirectoryRecord (group)calendar-dev>])
+ }
+ }
+
+ The field names that are indexed are defined by the C{indexedFields}
+ attribute of the service.
+
+ A subclass must override L{loadRecords}, which populates the index.
+
+ @cvar indexedFields: an iterable of field names (C{NamedConstant})
+ which are indexed.
"""
fieldName = ConstantsContainer(chain(
@@ -81,58 +165,95 @@
@property
def index(self):
+ """
+ Call L{loadRecords} and return the index.
+ """
self.loadRecords()
return self._index
- @index.setter
- def index(self, value):
- self._index = value
+ def loadRecords(self):
+ """
+ Load records. This method is called by the L{index} property and
+ provides a hook into which the index can be updated.
+ This method must be implemented by subclasses.
- def loadRecords(self):
+ An example implementation::
+
+ def loadRecords(self):
+ self.flush()
+ while True:
+ records = readSomeRecordsFromMyBackEnd()
+ if not records:
+ break
+ self.indexRecords(records)
"""
- Load records.
- """
raise NotImplementedError("Subclasses must implement loadRecords().")
+ def indexRecords(self, records):
+ """
+ Add some records to the index.
+
+ @param records: The records to index.
+ @type records: iterable of L{DirectoryRecord}
+ """
+ index = self._index
+
+ for fieldName in self.indexedFields:
+ index.setdefault(fieldName, {})
+
+ for record in records:
+ for fieldName in self.indexedFields:
+ values = record.fields.get(fieldName, None)
+
+ if values is not None:
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ for value in values:
+ index[fieldName].setdefault(value, set()).add(record)
+
+
def flush(self):
"""
Flush the index.
"""
- self._index = None
+ index = {}
+ for fieldName in self.indexedFields:
+ index.setdefault(fieldName, {})
- @staticmethod
- def _queryFlags(flags):
- predicate = lambda x: x
- normalize = lambda x: x
+ self._index = index
- if flags is not None:
- for flag in iterFlags(flags):
- if flag == MatchFlags.NOT:
- predicate = lambda x: not x
- elif flag == MatchFlags.caseInsensitive:
- normalize = lambda x: x.lower()
- else:
- raise NotImplementedError(
- "Unknown query flag: {0}".format(describe(flag))
- )
- return predicate, normalize
+ def indexedRecordsFromMatchExpression(self, expression, records=None):
+ """
+ Finds records in the internal indexes matching a single expression.
+ @param expression: An expression.
+ @type expression: L{MatchExpression}
- def indexedRecordsFromMatchExpression(self, expression, records=None):
+ @param records: a set of records to limit the search to. C{None} if
+ the whole directory should be searched.
+ @type records: L{set} or L{frozenset}
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{DirectoryRecord}s
"""
- Finds records in the internal indexes matching a single
- expression.
- @param expression: an expression
- @type expression: L{object}
- """
- predicate, normalize = self._queryFlags(expression.flags)
+ predicate = MatchFlags.predicator(expression.flags)
+ normalize = MatchFlags.normalizer(expression.flags)
- fieldIndex = self.index[expression.fieldName]
+ try:
+ fieldIndex = self.index[expression.fieldName]
+ except KeyError:
+ raise TypeError(
+ "indexedRecordsFromMatchExpression() was passed an "
+ "expression with an unindexed field: {0!r}"
+ .format(expression.fieldName)
+ )
+
matchValue = normalize(expression.fieldValue)
matchType = expression.matchType
@@ -156,27 +277,36 @@
)
else:
raise NotImplementedError(
- "Unknown match type: {0}".format(describe(matchType))
+ "Unknown match type: {0!r}".format(matchType)
)
matchingRecords = set()
for key in indexKeys:
matchingRecords |= fieldIndex.get(key, frozenset())
- if records is not None:
- matchingRecords &= records
+ # Not necessary, so don't unless we know it's a performance win:
+ # if records is not None:
+ # matchingRecords &= records
return succeed(matchingRecords)
def unIndexedRecordsFromMatchExpression(self, expression, records=None):
"""
- Finds records not in the internal indexes matching a single
- expression.
- @param expression: an expression
- @type expression: L{object}
+ Finds records not in the internal indexes matching a single expression.
+
+ @param expression: An expression.
+ @type expression: L{MatchExpression}
+
+ @param records: a set of records to limit the search to. C{None} if
+ the whole directory should be searched.
+ @type records: L{set} or L{frozenset}
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{DirectoryRecord}s
"""
- predicate, normalize = self._queryFlags(expression.flags)
+ predicate = MatchFlags.predicator(expression.flags)
+ normalize = MatchFlags.normalizer(expression.flags)
matchValue = normalize(expression.fieldValue)
matchType = expression.matchType
@@ -191,7 +321,7 @@
match = lambda fieldValue: predicate(fieldValue == matchValue)
else:
raise NotImplementedError(
- "Unknown match type: {0}".format(describe(matchType))
+ "Unknown match type: {0!r}".format(matchType)
)
result = set()
@@ -215,7 +345,11 @@
return succeed(result)
- def recordsFromExpression(self, expression, records=None):
+ def recordsFromNonCompoundExpression(self, expression, records=None):
+ """
+ This implementation can handle L{MatchExpression} expressions; other
+ expressions are passed up to the superclass.
+ """
if isinstance(expression, MatchExpression):
if expression.fieldName in self.indexedFields:
return self.indexedRecordsFromMatchExpression(
@@ -226,7 +360,7 @@
expression, records=records
)
else:
- return BaseDirectoryService.recordsFromExpression(
+ return BaseDirectoryService.recordsFromNonCompoundExpression(
self, expression, records=records
)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -22,14 +22,16 @@
from twisted.trial import unittest
from twext.who.idirectory import IDirectoryService, DirectoryConfigurationError
-from twext.who.aggregate import DirectoryService
+from twext.who.aggregate import DirectoryService, DirectoryRecord
from twext.who.util import ConstantsContainer
-
from twext.who.test import test_directory, test_xml
-from twext.who.test.test_xml import QueryMixIn, xmlService, TestService as XMLTestService
+from twext.who.test.test_xml import (
+ QueryMixIn, xmlService,
+ TestService as XMLTestService,
+ DirectoryServiceConvenienceTestMixIn
+)
-
class BaseTest(object):
def service(self, services=None):
if services is None:
@@ -48,18 +50,30 @@
class TestService(DirectoryService, QueryMixIn):
pass
- return TestService("xyzzy", services)
+ return TestService(u"xyzzy", services)
def xmlService(self, xmlData=None, serviceClass=None):
- return xmlService(self.mktemp(), xmlData, serviceClass)
+ return xmlService(
+ self.mktemp(),
+ xmlData=xmlData,
+ serviceClass=serviceClass
+ )
-class DirectoryServiceBaseTest(BaseTest, test_xml.DirectoryServiceBaseTest):
+class DirectoryServiceTest(
+ unittest.TestCase,
+ BaseTest,
+ DirectoryServiceConvenienceTestMixIn,
+ test_directory.BaseDirectoryServiceTest
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
def test_repr(self):
service = self.service()
- self.assertEquals(repr(service), "<TestService 'xyzzy'>")
+ self.assertEquals(repr(service), "<TestService u'xyzzy'>")
@@ -68,8 +82,12 @@
-class DirectoryServiceImmutableTest(BaseTest, test_directory.DirectoryServiceImmutableTest):
- pass
+class DirectoryServiceImmutableTest(
+ BaseTest,
+ test_directory.BaseDirectoryServiceImmutableTest,
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
@@ -81,24 +99,42 @@
class GroupsDirectoryService(XMLTestService):
recordType = ConstantsContainer((XMLTestService.recordType.group,))
- usersService = self.xmlService(testXMLConfigUsers, UsersDirectoryService)
- groupsService = self.xmlService(testXMLConfigGroups, GroupsDirectoryService)
+ usersService = self.xmlService(
+ xmlData=testXMLConfigUsers,
+ serviceClass=UsersDirectoryService
+ )
+ groupsService = self.xmlService(
+ xmlData=testXMLConfigGroups,
+ serviceClass=GroupsDirectoryService
+ )
- return BaseTest.service(self, (usersService, groupsService))
+ return BaseTest.service(
+ self,
+ services=(usersService, groupsService)
+ )
-class DirectoryServiceAggregatedBaseTest(AggregatedBaseTest, DirectoryServiceBaseTest):
+class DirectoryServiceAggregatedBaseTest(
+ AggregatedBaseTest,
+ DirectoryServiceTest,
+):
pass
-class DirectoryServiceAggregatedQueryTest(AggregatedBaseTest, test_xml.DirectoryServiceQueryTest):
+class DirectoryServiceAggregatedQueryTest(
+ AggregatedBaseTest,
+ test_xml.DirectoryServiceQueryTest,
+):
pass
-class DirectoryServiceAggregatedImmutableTest(AggregatedBaseTest, test_directory.DirectoryServiceImmutableTest):
+class DirectoryServiceAggregatedImmutableTest(
+ AggregatedBaseTest,
+ test_directory.BaseDirectoryServiceImmutableTest,
+):
pass
@@ -107,8 +143,8 @@
def test_conflictingRecordTypes(self):
self.assertRaises(
DirectoryConfigurationError,
- BaseTest.service, self,
- (self.xmlService(), self.xmlService(testXMLConfigUsers)),
+ self.service,
+ services=(self.xmlService(), self.xmlService(testXMLConfigUsers)),
)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,32 +18,96 @@
Generic directory service base implementation tests.
"""
+from uuid import UUID
+from textwrap import dedent
+
from zope.interface.verify import verifyObject, BrokenMethodImplementation
+from twisted.python.constants import Names, NamedConstant
from twisted.trial import unittest
-from twisted.trial.unittest import SkipTest
from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import succeed
from twext.who.idirectory import QueryNotSupportedError, NotAllowedError
from twext.who.idirectory import RecordType, FieldName
from twext.who.idirectory import IDirectoryService, IDirectoryRecord
+from twext.who.expression import CompoundExpression, Operand
from twext.who.directory import DirectoryService, DirectoryRecord
-class BaseTest(unittest.TestCase):
- realmName = "xyzzy"
+class StubDirectoryService(DirectoryService):
+ """
+ Stub directory service with some built-in records and an implementation
+ of C{recordsFromNonCompoundExpression}.
+ """
+ def __init__(self, realmName):
+ DirectoryService.__init__(self, realmName)
- def service(self):
- if not hasattr(self, "_service"):
- self._service = DirectoryService(self.realmName)
- return self._service
+ self.records = RecordStorage(self, DirectoryRecord)
+ def recordsFromExpression(self, expression):
+ self.seenExpressions = []
-class DirectoryServiceTest(BaseTest):
+ return DirectoryService.recordsFromExpression(self, expression)
+
+
+ def recordsFromNonCompoundExpression(self, expression, records=None):
+ """
+ This implementation handles three expressions:
+
+ The expression C{u"None"} will match no records.
+
+ The expressions C{u"twistedmatrix.com"} and C{u"calendarserver.org"}
+ will match records that have an email address ending with the
+ given expression.
+ """
+ self.seenExpressions.append(expression)
+
+ if expression == u"None":
+ return succeed([])
+
+ if expression in (u"twistedmatrix.com", u"calendarserver.org"):
+ result = []
+ for record in self.records:
+ for email in record.emailAddresses:
+ if email.endswith(expression):
+ result.append(record)
+ break
+ return succeed(result)
+
+ return DirectoryService.recordsFromNonCompoundExpression(
+ self, expression, records=records
+ )
+
+
+
+class ServiceMixIn(object):
+ """
+ MixIn that sets up a service appropriate for testing.
+ """
+
+ realmName = u"xyzzy"
+
+
+ def service(self, subClass=None):
+ if subClass is None:
+ subClass = self.serviceClass
+ return subClass(self.realmName)
+
+
+
+class BaseDirectoryServiceTest(ServiceMixIn):
+ """
+ Tests for directory services.
+ """
+
def test_interface(self):
+ """
+ Service instance conforms to L{IDirectoryService}.
+ """
service = self.service()
try:
verifyObject(IDirectoryService, service)
@@ -52,16 +116,30 @@
def test_init(self):
+ """
+ Test initialization.
+ """
service = self.service()
self.assertEquals(service.realmName, self.realmName)
def test_repr(self):
+ """
+ L{DirectoryService.repr} returns the expected string.
+ """
service = self.service()
- self.assertEquals(repr(service), "<DirectoryService 'xyzzy'>")
+ self.assertEquals(
+ repr(service),
+ "<{0} u'xyzzy'>".format(self.serviceClass.__name__)
+ )
def test_recordTypes(self):
+ """
+ L{DirectoryService.recordTypes} returns the supported set of record
+ types. For L{DirectoryService}, that's the set of constants in the
+ L{DirectoryService.recordType} attribute.
+ """
service = self.service()
self.assertEquals(
set(service.recordTypes()),
@@ -69,102 +147,451 @@
)
+ def test_recordsFromNonCompoundExpression_unknownExpression(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ self.assertFailure(
+ service.recordsFromNonCompoundExpression(object()),
+ QueryNotSupportedError
+ )
+
+
@inlineCallbacks
- def test_recordsFromQueryNone(self):
+ def test_recordsFromNonCompoundExpression_emptyRecords(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type and an empty C{records} set returns an empty result.
+ """
service = self.service()
- records = (yield service.recordsFromQuery(()))
- for record in records:
- self.failTest("No records expected")
+ result = (
+ yield service.recordsFromNonCompoundExpression(
+ object(), records=()
+ )
+ )
+ self.assertEquals(set(result), set(()))
- def test_recordsFromQueryBogus(self):
+ def test_recordsFromNonCompoundExpression_nonEmptyRecords(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type and a non-empty C{records} fails with
+ L{QueryNotSupportedError}.
+ """
service = self.service()
- self.assertFailure(service.recordsFromQuery((object(),)), QueryNotSupportedError)
+ wsanchez = self.directoryRecordClass(
+ service,
+ {
+ service.fieldName.recordType: service.recordType.user,
+ service.fieldName.uid: u"__wsanchez__",
+ service.fieldName.shortNames: [u"wsanchez"],
+ }
+ )
+ self.assertFailure(
+ service.recordsFromNonCompoundExpression(
+ object(), records=((wsanchez,))
+ ),
+ QueryNotSupportedError
+ )
+
+
+ def test_recordsFromExpression_unknownExpression(self):
+ """
+ L{DirectoryService.recordsFromExpression} with an unknown expression
+ type fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ result = yield(service.recordsFromExpression(object()))
+ self.assertFailure(result, QueryNotSupportedError)
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_emptyExpression(self):
+ """
+ L{DirectoryService.recordsFromExpression} with an unknown expression
+ type and an empty L{CompoundExpression} returns an empty result.
+ """
+ service = self.service()
+
+ for operand in Operand.iterconstants():
+ result = yield(service.recordsFromExpression(
+ CompoundExpression((), operand)
+ ))
+ self.assertEquals(set(result), set(()))
+
+
+ def _unimplemented(self):
+ """
+ Unimplemented test.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
+ test_recordWithUID = _unimplemented
+ test_recordWithGUID = _unimplemented
+ test_recordsWithRecordType = _unimplemented
+ test_recordWithShortName = _unimplemented
+ test_recordsWithEmailAddress = _unimplemented
+
+
+ def test_updateRecordsEmpty(self):
+ """
+ Updating no records is not an error.
+ """
+ service = self.service()
+ for create in (True, False):
+ service.updateRecords((), create=create),
+
+
+ def test_removeRecordsEmpty(self):
+ """
+ Removing no records is allowed.
+ """
+ service = self.service()
+
+ service.removeRecords(())
+
+
+
+class DirectoryServiceRecordsFromExpressionTest(
+ unittest.TestCase,
+ ServiceMixIn
+):
+ """
+ Tests for L{DirectoryService.recordsFromExpression}.
+ """
+ serviceClass = StubDirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ @inlineCallbacks
+ def test_recordsFromExpression_single(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a single expression.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression("twistedmatrix.com")
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__exarkun__",
+ u"__dreid__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_OR(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.OR}.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ Operand.OR
+ )
+ )
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__sagen__",
+ u"__cdaboo__",
+ u"__dre__",
+ u"__exarkun__",
+ u"__dreid__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_AND(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.AND}.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ Operand.AND
+ )
+ )
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_AND_optimized(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.AND}, and when one of the
+ expression matches no records, the subsequent expressions are skipped.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"None",
+ u"calendarserver.org",
+ ),
+ Operand.AND
+ )
+ )
+
+ self.assertEquals(
+ set(()),
+ set((record.uid for record in result))
+ )
+
+ self.assertEquals(
+ [u"twistedmatrix.com", u"None"],
+ service.seenExpressions
+ )
+
+
+ def test_recordsFromExpression_unknownOperand(self):
+ """
+ L{DirectoryService.recordsFromExpression} fails with
+ L{QueryNotSupportedError} when given a L{CompoundExpression} with an
+ unknown operand.
+ """
+ service = self.service()
+
+ results = service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ WackyOperand.WHUH
+ )
+ )
+
+ self.assertFailure(results, QueryNotSupportedError)
+
+
+
+class DirectoryServiceConvenienceTest(
+ unittest.TestCase,
+ BaseDirectoryServiceTest
+):
+ """
+ Tests for L{DirectoryService} convenience methods.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
def test_recordWithUID(self):
- raise SkipTest("Subclasses should implement this test.")
+ """
+ L{DirectoryService.recordWithUID} fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ self.assertFailure(
+ service.recordWithUID(u""),
+ QueryNotSupportedError
+ )
+
def test_recordWithGUID(self):
- raise SkipTest("Subclasses should implement this test.")
+ """
+ L{DirectoryService.recordWithGUID} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ self.assertFailure(
+ service.recordWithGUID(UUID(int=0)),
+ QueryNotSupportedError
+ )
+
def test_recordsWithRecordType(self):
- raise SkipTest("Subclasses should implement this test.")
+ """
+ L{DirectoryService.recordsWithRecordType} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ for recordType in RecordType.iterconstants():
+ self.assertFailure(
+ service.recordsWithRecordType(recordType),
+ QueryNotSupportedError
+ )
+
def test_recordWithShortName(self):
- raise SkipTest("Subclasses should implement this test.")
+ """
+ L{DirectoryService.recordWithShortName} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ for recordType in RecordType.iterconstants():
+ self.assertFailure(
+ service.recordWithShortName(recordType, u""),
+ QueryNotSupportedError
+ )
+
def test_recordsWithEmailAddress(self):
- raise SkipTest("Subclasses should implement this test.")
+ """
+ L{DirectoryService.recordsWithEmailAddress} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ self.assertFailure(
+ service.recordsWithEmailAddress("a at b"),
+ QueryNotSupportedError
+ )
-class DirectoryServiceImmutableTest(BaseTest):
+
+class BaseDirectoryServiceImmutableTest(ServiceMixIn):
+ """
+ Tests for immutable directory services.
+ """
+
def test_updateRecordsNotAllowed(self):
+ """
+ Updating records is not allowed.
+ """
service = self.service()
- newRecord = DirectoryRecord(
+ newRecord = self.directoryRecordClass(
service,
- fields = {
- service.fieldName.uid: "__plugh__",
+ fields={
+ service.fieldName.uid: u"__plugh__",
service.fieldName.recordType: service.recordType.user,
- service.fieldName.shortNames: ("plugh",),
+ service.fieldName.shortNames: (u"plugh",),
}
)
- self.assertFailure(
- service.updateRecords((newRecord,), create=True),
- NotAllowedError,
- )
+ for create in (True, False):
+ self.assertFailure(
+ service.updateRecords((newRecord,), create=create),
+ NotAllowedError,
+ )
- self.assertFailure(
- service.updateRecords((newRecord,), create=False),
- NotAllowedError,
- )
-
def test_removeRecordsNotAllowed(self):
+ """
+ Removing records is not allowed.
+ """
service = self.service()
- service.removeRecords(())
self.assertFailure(
- service.removeRecords(("foo",)),
+ service.removeRecords((u"foo",)),
NotAllowedError,
)
-class DirectoryRecordTest(BaseTest):
+class DirectoryServiceImmutableTest(
+ unittest.TestCase,
+ BaseDirectoryServiceImmutableTest,
+):
+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(ServiceMixIn):
+ """
+ Tests for directory records.
+ """
+
fields_wsanchez = {
- FieldName.uid: "UID:wsanchez",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("wsanchez", "wilfredo_sanchez"),
- FieldName.fullNames: ("Wilfredo Sanchez", "Wilfredo Sanchez Vega"),
- FieldName.emailAddresses: ("wsanchez at calendarserver.org", "wsanchez at example.com")
+ FieldName.uid: u"UID:wsanchez",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"wsanchez", u"wilfredo_sanchez"),
+ FieldName.fullNames: (
+ u"Wilfredo Sanchez",
+ u"Wilfredo Sanchez Vega",
+ ),
+ FieldName.emailAddresses: (
+ u"wsanchez at calendarserver.org",
+ u"wsanchez at example.com",
+ )
}
fields_glyph = {
- FieldName.uid: "UID:glyph",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("glyph",),
- FieldName.fullNames: ("Glyph Lefkowitz",),
- FieldName.emailAddresses: ("glyph at calendarserver.org",)
+ FieldName.uid: u"UID:glyph",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"glyph",),
+ FieldName.fullNames: (u"Glyph Lefkowitz",),
+ FieldName.emailAddresses: (u"glyph at calendarserver.org",)
}
fields_sagen = {
- FieldName.uid: "UID:sagen",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("sagen",),
- FieldName.fullNames: ("Morgen Sagen",),
- FieldName.emailAddresses: ("sagen at CalendarServer.org",)
+ FieldName.uid: u"UID:sagen",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"sagen",),
+ FieldName.fullNames: (u"Morgen Sagen",),
+ FieldName.emailAddresses: (u"sagen at CalendarServer.org",)
}
+ fields_nobody = {
+ FieldName.uid: u"UID:nobody",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"nobody",),
+ }
- def _testRecord(self, fields=None, service=None):
+ fields_staff = {
+ FieldName.uid: u"UID:staff",
+ FieldName.recordType: RecordType.group,
+ FieldName.shortNames: (u"staff",),
+ FieldName.fullNames: (u"Staff",),
+ FieldName.emailAddresses: (u"staff at CalendarServer.org",)
+ }
+
+
+ def makeRecord(self, fields=None, service=None):
+ """
+ Create a directory record from fields and a service.
+
+ @param fields: Record fields.
+ @type fields: L{dict} with L{FieldName} keys
+
+ @param service: Directory service.
+ @type service: L{DirectoryService}
+
+ @return: A directory record.
+ @rtype: L{DirectoryRecord}
+ """
if fields is None:
fields = self.fields_wsanchez
if service is None:
@@ -173,7 +600,10 @@
def test_interface(self):
- record = self._testRecord()
+ """
+ L{DirectoryRecord} complies with L{IDirectoryRecord}.
+ """
+ record = self.makeRecord()
try:
verifyObject(IDirectoryRecord, record)
except BrokenMethodImplementation as e:
@@ -181,100 +611,348 @@
def test_init(self):
+ """
+ L{DirectoryRecord} initialization sets service and fields.
+ """
service = self.service()
- wsanchez = self._testRecord(self.fields_wsanchez, service=service)
+ wsanchez = self.makeRecord(self.fields_wsanchez, service=service)
self.assertEquals(wsanchez.service, service)
- self.assertEquals(wsanchez.fields , self.fields_wsanchez)
+ self.assertEquals(wsanchez.fields, self.fields_wsanchez)
def test_initWithNoUID(self):
+ """
+ Directory records must have a UID.
+ """
fields = self.fields_wsanchez.copy()
del fields[FieldName.uid]
- self.assertRaises(ValueError, self._testRecord, fields)
+ self.assertRaises(ValueError, self.makeRecord, fields)
fields = self.fields_wsanchez.copy()
- fields[FieldName.uid] = ""
- self.assertRaises(ValueError, self._testRecord, fields)
+ fields[FieldName.uid] = u""
+ self.assertRaises(ValueError, self.makeRecord, fields)
def test_initWithNoRecordType(self):
+ """
+ Directory records must have a record type.
+ """
fields = self.fields_wsanchez.copy()
del fields[FieldName.recordType]
- self.assertRaises(ValueError, self._testRecord, fields)
+ self.assertRaises(ValueError, self.makeRecord, fields)
fields = self.fields_wsanchez.copy()
- fields[FieldName.recordType] = ""
- self.assertRaises(ValueError, self._testRecord, fields)
+ fields[FieldName.recordType] = None
+ self.assertRaises(ValueError, self.makeRecord, fields)
+ def test_initWithBogusRecordType(self):
+ """
+ Directory records must have a known record type.
+ """
+ fields = self.fields_wsanchez.copy()
+ fields[FieldName.recordType] = object()
+ self.assertRaises(ValueError, self.makeRecord, fields)
+
+
def test_initWithNoShortNames(self):
+ """
+ Directory records must have a short name.
+ """
fields = self.fields_wsanchez.copy()
del fields[FieldName.shortNames]
- self.assertRaises(ValueError, self._testRecord, fields)
+ self.assertRaises(ValueError, self.makeRecord, fields)
fields = self.fields_wsanchez.copy()
fields[FieldName.shortNames] = ()
- self.assertRaises(ValueError, self._testRecord, fields)
+ self.assertRaises(ValueError, self.makeRecord, fields)
fields = self.fields_wsanchez.copy()
- fields[FieldName.shortNames] = ("",)
- self.assertRaises(ValueError, self._testRecord, fields)
+ fields[FieldName.shortNames] = (u"",)
+ self.assertRaises(ValueError, self.makeRecord, fields)
fields = self.fields_wsanchez.copy()
- fields[FieldName.shortNames] = ("wsanchez", "")
- self.assertRaises(ValueError, self._testRecord, fields)
+ fields[FieldName.shortNames] = (u"wsanchez", u"")
+ self.assertRaises(ValueError, self.makeRecord, fields)
- def test_initWithBogusRecordType(self):
- fields = self.fields_wsanchez.copy()
- fields[FieldName.recordType] = object()
- self.assertRaises(ValueError, self._testRecord, fields)
+ def test_initNormalizeEmailLowercase(self):
+ """
+ Email addresses are normalized to lowercase.
+ """
+ sagen = self.makeRecord(self.fields_sagen)
+ self.assertEquals(
+ sagen.fields[FieldName.emailAddresses],
+ (u"sagen at calendarserver.org",)
+ )
- def test_initNormalize(self):
- sagen = self._testRecord(self.fields_sagen)
+ def test_repr(self):
+ """
+ L{DirectoryRecord.repr} returns the expected string.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
+
self.assertEquals(
- sagen.fields[FieldName.emailAddresses],
- ("sagen at calendarserver.org",)
+ "<DirectoryRecord (user)wsanchez>",
+ repr(wsanchez)
)
def test_compare(self):
+ """
+ Comparison of records.
+ """
fields_glyphmod = self.fields_glyph.copy()
del fields_glyphmod[FieldName.emailAddresses]
- wsanchez = self._testRecord(self.fields_wsanchez)
- wsanchezmod = self._testRecord(self.fields_wsanchez, DirectoryService("plugh"))
- glyph = self._testRecord(self.fields_glyph)
- glyphmod = self._testRecord(fields_glyphmod)
+ plugh = DirectoryService(u"plugh")
+ wsanchez = self.makeRecord(self.fields_wsanchez)
+ wsanchezmod = self.makeRecord(self.fields_wsanchez, plugh)
+ glyph = self.makeRecord(self.fields_glyph)
+ glyphmod = self.makeRecord(fields_glyphmod)
+
self.assertEquals(wsanchez, wsanchez)
self.assertNotEqual(wsanchez, glyph)
- self.assertNotEqual(glyph, glyphmod) # UID matches, other fields do not
+ self.assertNotEqual(glyph, glyphmod) # UID matches, other fields don't
self.assertNotEqual(glyphmod, wsanchez)
- self.assertNotEqual(wsanchez, wsanchezmod) # Different service
+ self.assertNotEqual(wsanchez, wsanchezmod) # Different service
def test_attributeAccess(self):
- wsanchez = self._testRecord(self.fields_wsanchez)
+ """
+ Fields can be accessed as attributes.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
- self.assertEquals(wsanchez.recordType , wsanchez.fields[FieldName.recordType ])
- self.assertEquals(wsanchez.uid , wsanchez.fields[FieldName.uid ])
- self.assertEquals(wsanchez.shortNames , wsanchez.fields[FieldName.shortNames ])
- self.assertEquals(wsanchez.emailAddresses, wsanchez.fields[FieldName.emailAddresses])
+ self.assertEquals(
+ wsanchez.recordType,
+ wsanchez.fields[FieldName.recordType]
+ )
+ self.assertEquals(
+ wsanchez.uid,
+ wsanchez.fields[FieldName.uid]
+ )
+ self.assertEquals(
+ wsanchez.shortNames,
+ wsanchez.fields[FieldName.shortNames]
+ )
+ self.assertEquals(
+ wsanchez.emailAddresses,
+ wsanchez.fields[FieldName.emailAddresses]
+ )
+ self.assertRaises(AttributeError, lambda: wsanchez.fooBarBaz)
+
+ nobody = self.makeRecord(self.fields_nobody)
+
+ self.assertRaises(AttributeError, lambda: nobody.emailAddresses)
+
+
+ def test_description(self):
+ """
+ L{DirectoryRecord.description} returns the expected string.
+ """
+ sagen = self.makeRecord(self.fields_sagen)
+
+ self.assertEquals(
+ dedent(
+ u"""
+ DirectoryRecord:
+ UID = UID:sagen
+ record type = user
+ short names = (u'sagen',)
+ full names = (u'Morgen Sagen',)
+ email addresses = ('sagen at calendarserver.org',)
+ """[1:]
+ ),
+ sagen.description()
+ )
+
+ test_description.todo = "Intermittent order issues"
+
+
+ def test_members_group(self):
+ """
+ Group members for group records.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
@inlineCallbacks
- def test_members(self):
- wsanchez = self._testRecord(self.fields_wsanchez)
+ def test_members_nonGroup(self):
+ """
+ Group members for non-group records. Non-groups have no members.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
self.assertEquals(
set((yield wsanchez.members())),
set()
)
- raise SkipTest("Subclasses should implement this test.")
- def test_groups(self):
- raise SkipTest("Subclasses should implement this test.")
+ def test_memberships(self):
+ """
+ Group memberships.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+ """
+ Tests for L{DirectoryRecord}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ def test_members_group(self):
+ staff = self.makeRecord(self.fields_staff)
+
+ self.assertFailure(staff.members(), NotImplementedError)
+
+
+ def test_memberships(self):
+ wsanchez = self.makeRecord(self.fields_wsanchez)
+
+ self.assertFailure(wsanchez.groups(), NotImplementedError)
+
+
+
+class RecordStorage(object):
+ """
+ Container for directory records.
+ """
+ def __init__(self, service, recordClass):
+ self.service = service
+ self.recordClass = recordClass
+ self.records = []
+
+ self.addDefaultRecords()
+
+
+ def addDefaultRecords(self):
+ """
+ Add a known set of records to this service.
+ """
+ self.addUser(
+ shortNames=[u"wsanchez", u"wilfredo_sanchez"],
+ fullNames=[
+ u"Wilfredo S\xe1nchez Vega",
+ u"Wilfredo Sanchez Vega",
+ u"Wilfredo Sanchez",
+ ],
+ emailAddresses=[
+ u"wsanchez at bitbucket.calendarserver.org",
+ u"wsanchez at devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"glyph"],
+ fullNames=[u"Glyph Lefkowitz"],
+ emailAddresses=[
+ u"glyph at bitbucket.calendarserver.org",
+ u"glyph at devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"sagen"],
+ fullNames=[u"Morgen Sagen"],
+ emailAddresses=[
+ u"sagen at bitbucket.calendarserver.org",
+ u"shared at example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"cdaboo"],
+ fullNames=[u"Cyrus Daboo"],
+ emailAddresses=[
+ u"cdaboo at bitbucket.calendarserver.org",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"dre"],
+ fullNames=[u"Andre LaBranche"],
+ emailAddresses=[
+ u"dre at bitbucket.calendarserver.org",
+ u"shared at example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"exarkun"],
+ fullNames=[u"Jean-Paul Calderone"],
+ emailAddresses=[
+ u"exarkun at devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"dreid"],
+ fullNames=[u"David Reid"],
+ emailAddresses=[
+ u"dreid at devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"joe"],
+ fullNames=[u"Joe Schmoe"],
+ emailAddresses=[
+ u"joe at example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"alyssa"],
+ fullNames=[u"Alyssa P. Hacker"],
+ emailAddresses=[
+ u"alyssa at example.com",
+ ],
+ )
+
+
+ def addUser(self, shortNames, fullNames, emailAddresses=[]):
+ """
+ Add a user record with the given field information.
+
+ @param shortNames: Record short names.
+ @type shortNames: L{list} of L{unicode}s
+
+ @param fullNames: Record full names.
+ @type fullNames: L{list} of L{unicode}s
+
+ @param emailAddresses: Record email addresses.
+ @type emailAddresses: L{list} of L{unicode}s
+ """
+ service = self.service
+ fieldName = service.fieldName
+ recordType = service.recordType
+ self.records.append(self.recordClass(self.service, {
+ fieldName.recordType: recordType.user,
+ fieldName.uid: u"__{0}__".format(shortNames[0]),
+ fieldName.shortNames: shortNames,
+ fieldName.fullNames: fullNames,
+ fieldName.password: u"".join(reversed(shortNames[0])),
+ fieldName.emailAddresses: emailAddresses,
+ }))
+
+
+ def __iter__(self):
+ return iter(self.records)
+
+
+
+class WackyOperand(Names):
+ """
+ Wacky operands.
+ """
+ WHUH = NamedConstant()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -26,29 +26,31 @@
class MatchExpressionTest(unittest.TestCase):
- def test_repr(self):
+ def test_repr_name(self):
self.assertEquals(
- "<MatchExpression: 'full names' equals 'Wilfredo Sanchez'>",
+ "<MatchExpression: u'full names' equals u'Wilfredo Sanchez'>",
repr(MatchExpression(
FieldName.fullNames,
- "Wilfredo Sanchez",
+ u"Wilfredo Sanchez",
)),
)
+ def test_repr_type(self):
self.assertEquals(
- "<MatchExpression: 'full names' contains 'Sanchez'>",
+ "<MatchExpression: u'full names' contains u'Sanchez'>",
repr(MatchExpression(
FieldName.fullNames,
- "Sanchez",
+ u"Sanchez",
matchType=MatchType.contains,
)),
)
+ def test_repr_flags(self):
self.assertEquals(
- "<MatchExpression: 'full names' starts with 'Wilfredo' (not)>",
+ "<MatchExpression: u'full names' starts with u'Wilfredo' (not)>",
repr(MatchExpression(
FieldName.fullNames,
- "Wilfredo",
+ u"Wilfredo",
matchType=MatchType.startsWith,
flags=MatchFlags.NOT,
)),
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py (from rev 12016, CalendarServer/trunk/twext/who/test/test_index.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,486 @@
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Indexed directory service base implementation tests.
+"""
+
+from twisted.trial import unittest
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+from twext.who.idirectory import FieldName as BaseFieldName
+from twext.who.idirectory import QueryNotSupportedError
+from twext.who.expression import MatchExpression, MatchType
+from twext.who.index import DirectoryService, DirectoryRecord
+from twext.who.test import test_directory
+from twext.who.test.test_directory import RecordStorage
+
+
+
+def noLoadDirectoryService(superClass):
+ """
+ Creates an indexed directory service that has a no-op implementation of
+ L{DirectoryService.loadRecords}.
+
+ @param superClass: The superclass of the new service.
+ @type superClass: subclass of L{DirectoryService}
+
+ @return: A new directory service class.
+ @rtype: subclass of C{superClass}
+ """
+ assert issubclass(superClass, DirectoryService)
+
+ class NoLoadDirectoryService(superClass):
+ def loadRecords(self):
+ pass
+
+ def indexedRecordsFromMatchExpression(self, *args, **kwargs):
+ self._calledIndexed = True
+ return superClass.indexedRecordsFromMatchExpression(
+ self, *args, **kwargs
+ )
+
+ def unIndexedRecordsFromMatchExpression(self, *args, **kwargs):
+ self._calledUnindexed = True
+ return superClass.unIndexedRecordsFromMatchExpression(
+ self, *args, **kwargs
+ )
+
+ return NoLoadDirectoryService
+
+
+class BaseDirectoryServiceTest(test_directory.BaseDirectoryServiceTest):
+ """
+ Tests for indexed directory services.
+ """
+
+ def noLoadServicePopulated(self):
+ service = self.service(
+ subClass=noLoadDirectoryService(self.serviceClass)
+ )
+
+ records = RecordStorage(service, DirectoryRecord)
+ service.indexRecords(records)
+ service.records = records
+
+ return service
+
+ def test_indexRecords_positive(self):
+ """
+ L{DirectoryService.indexRecords} ensures all record data is in the
+ index.
+ """
+ service = self.noLoadServicePopulated()
+ index = service.index
+
+ # Verify that the fields that should be indexed are, in fact, indexed
+ # for each record.
+ for record in service.records:
+ for fieldName in service.indexedFields:
+ values = record.fields.get(fieldName, None)
+
+ if values is None:
+ continue
+
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ for value in values:
+ self.assertIn(fieldName, index)
+ self.assertIn(value, index[fieldName])
+
+ indexedRecords = index[fieldName][value]
+ self.assertIn(record, indexedRecords)
+
+
+ def test_indexRecords_negative(self):
+ """
+ L{DirectoryService.indexRecords} does not have extra data in the index.
+ """
+ service = self.noLoadServicePopulated()
+ index = service.index
+
+ # Verify that all data in the index cooresponds to the records passed
+ # in.
+ for fieldName, fieldIndex in index.iteritems():
+ for fieldValue, records in fieldIndex.iteritems():
+ for record in records:
+ self.assertIn(fieldName, record.fields)
+ values = record.fields[fieldName]
+
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ self.assertIn(fieldValue, values)
+
+
+ def test_flush(self):
+ """
+ C{flush} empties the index.
+ """
+ service = self.noLoadServicePopulated()
+
+ self.assertFalse(emptyIndex(service.index)) # Test the test
+ service.flush()
+ self.assertTrue(emptyIndex(service.index))
+
+
+ @inlineCallbacks
+ def _test_indexedRecordsFromMatchExpression(
+ self, inOut, matchType, fieldName=BaseFieldName.shortNames,
+ ):
+ service = self.noLoadServicePopulated()
+
+ for subString, uids in (inOut):
+ records = yield service.indexedRecordsFromMatchExpression(
+ MatchExpression(
+ fieldName, subString,
+ matchType
+ )
+ )
+ self.assertEquals(
+ set((record.uid for record in records)),
+ set(uids)
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_startsWith(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a startsWith
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"w", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"sage", (u"__sagen__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"sanch", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"agen", (u"__sagen__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)), # MultiValue
+ (u"dre", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_notIndexed(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an
+ unindexed field name.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.password
+ )
+ self.assertFailure(result, TypeError)
+
+
+ def test_indexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ self.assertFailure(result, NotImplementedError)
+
+
+ @inlineCallbacks
+ def _test_unIndexedRecordsFromMatchExpression(
+ self, inOut, matchType, fieldName=BaseFieldName.fullNames,
+ ):
+ service = self.noLoadServicePopulated()
+
+ for subString, uids in (inOut):
+ records = yield service.unIndexedRecordsFromMatchExpression(
+ MatchExpression(
+ fieldName, subString,
+ matchType
+ )
+ )
+ self.assertEquals(
+ set((record.uid for record in records)),
+ set(uids)
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_startsWith(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ startsWith expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"Andre", (u"__dre__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Sanchez", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"LaBra", (u"__dre__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo Sanchez", (u"__wsanchez__",)), # MultiValue
+ (u"Andre LaBranche", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_indexed(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an
+ indexed field name.
+ """
+ self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.shortNames
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ self.assertFailure(result, NotImplementedError)
+
+
+ @inlineCallbacks
+ def _test_recordsFromNonCompoundExpression(self, expression):
+ service = self.noLoadServicePopulated()
+ yield service.recordsFromNonCompoundExpression(expression)
+ returnValue(service)
+
+
+ @inlineCallbacks
+ def test_recordsFromNonCompoundExpression_match_indexed(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an indexed field calls
+ L{DirectoryRecord.indexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.shortNames, u"...")
+ )
+ self.assertTrue(getattr(service, "_calledIndexed", False))
+ self.assertFalse(getattr(service, "_calledUnindexed", False))
+
+
+ @inlineCallbacks
+ def test_recordsFromNonCompoundExpression_match_unindexed(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an unindexed field calls
+ L{DirectoryRecord.unIndexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.password, u"...")
+ )
+ self.assertFalse(getattr(service, "_calledIndexed", False))
+ self.assertTrue(getattr(service, "_calledUnindexed", False))
+
+
+ def test_recordsFromNonCompoundExpression_unknown(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ an unknown expression calls superclass, which will result in a
+ L{QueryNotSupportedError}.
+ """
+ result = self._test_recordsFromNonCompoundExpression(object())
+ self.assertFailure(result, QueryNotSupportedError)
+
+
+
+class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
+ """
+ Tests for L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def test_init_noIndex(self):
+ """
+ Index starts as C{None}.
+ """
+ service = self.service()
+ self.assertTrue(emptyIndex(service._index))
+
+
+ def test_index_get(self):
+ """
+ Getting the C{index} property calls C{loadRecords}.
+ """
+ class TestService(DirectoryService):
+ loaded = False
+
+ def loadRecords(self):
+ self.loaded = True
+
+ service = TestService(u"")
+ service.index
+ self.assertTrue(service.loaded)
+
+
+ def test_loadRecords(self):
+ """
+ L{DirectoryService.loadRecords} raises C{NotImplementedError}.
+ """
+ service = self.service()
+ self.assertRaises(NotImplementedError, service.loadRecords)
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_recordWithUID = _noop
+ test_recordWithGUID = _noop
+ test_recordsWithRecordType = _noop
+ test_recordWithShortName = _noop
+ test_recordsWithEmailAddress = _noop
+
+
+
+class BaseDirectoryServiceImmutableTest(
+ test_directory.BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable indexed directory services.
+ """
+
+
+
+class DirectoryServiceImmutableTest(
+ unittest.TestCase, BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(test_directory.BaseDirectoryRecordTest):
+ """
+ Tests for indexed directory records.
+ """
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+ """
+ Tests for L{DirectoryRecord}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_members_group = _noop
+ test_memberships = _noop
+
+
+
+def emptyIndex(index):
+ """
+ Determine whether an index is empty.
+
+ @param index: An index.
+ @type index: L{dict}
+
+ @return: true if C{index} is empty, otherwise false.
+ """
+ if not index:
+ return True
+
+ for fieldName, fieldIndex in index.iteritems():
+ for fieldValue, records in fieldIndex.iteritems():
+ for record in records:
+ return False
+
+ return True
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -29,11 +29,11 @@
class Tools(Names):
- hammer = NamedConstant()
+ hammer = NamedConstant()
screwdriver = NamedConstant()
- hammer.description = "nail pounder"
- screwdriver.description = "screw twister"
+ hammer.description = u"nail pounder"
+ screwdriver.description = u"screw twister"
@@ -48,9 +48,9 @@
g = FlagConstant()
b = FlagConstant()
- r.description = "red"
- g.description = "green"
- b.description = "blue"
+ r.description = u"red"
+ g.description = u"green"
+ b.description = u"blue"
black = FlagConstant()
@@ -82,7 +82,11 @@
)
def test_lookupByName(self):
- constants = set((Instruments.hammer, Tools.screwdriver, Instruments.chisel))
+ constants = set((
+ Instruments.hammer,
+ Tools.screwdriver,
+ Instruments.chisel,
+ ))
container = ConstantsContainer(constants)
self.assertEquals(
@@ -108,13 +112,13 @@
class UtilTest(unittest.TestCase):
def test_uniqueResult(self):
self.assertEquals(1, uniqueResult((1,)))
- self.assertRaises(DirectoryServiceError, uniqueResult, (1,2,3))
+ self.assertRaises(DirectoryServiceError, uniqueResult, (1, 2, 3))
def test_describe(self):
- self.assertEquals("nail pounder", describe(Tools.hammer))
- self.assertEquals("hammer", describe(Instruments.hammer))
+ self.assertEquals(u"nail pounder", describe(Tools.hammer))
+ self.assertEquals(u"hammer", describe(Instruments.hammer))
def test_describeFlags(self):
- self.assertEquals("blue", describe(Switches.b))
- self.assertEquals("red|green", describe(Switches.r|Switches.g))
- self.assertEquals("blue|black", describe(Switches.b|Switches.black))
+ self.assertEquals(u"blue", describe(Switches.b))
+ self.assertEquals(u"red|green", describe(Switches.r | Switches.g))
+ self.assertEquals(u"blue|black", describe(Switches.b | Switches.black))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,26 +19,31 @@
"""
from time import sleep
+from uuid import UUID
+from textwrap import dedent
from twisted.trial import unittest
from twisted.python.filepath import FilePath
from twisted.internet.defer import inlineCallbacks
from twext.who.idirectory import NoSuchRecordError
-from twext.who.idirectory import Operand
+from twext.who.expression import CompoundExpression, Operand
from twext.who.expression import MatchExpression, MatchType, MatchFlags
from twext.who.xml import ParseError
from twext.who.xml import DirectoryService, DirectoryRecord
+from twext.who.test import test_index
-from twext.who.test import test_directory
+class BaseTest(object):
+ def service(self, subClass=None, xmlData=None):
+ return xmlService(
+ self.mktemp(),
+ xmlData=xmlData,
+ serviceClass=subClass
+ )
-class BaseTest(unittest.TestCase):
- def service(self, xmlData=None):
- return xmlService(self.mktemp(), xmlData)
-
def assertRecords(self, records, uids):
self.assertEquals(
frozenset((record.uid for record in records)),
@@ -47,30 +52,26 @@
-class DirectoryServiceBaseTest(BaseTest, test_directory.DirectoryServiceTest):
- def test_repr(self):
- service = self.service()
-
- self.assertEquals(repr(service), "<TestService (not loaded)>")
- service.loadRecords()
- self.assertEquals(repr(service), "<TestService 'xyzzy'>")
-
-
+class DirectoryServiceConvenienceTestMixIn(BaseTest):
@inlineCallbacks
def test_recordWithUID(self):
service = self.service()
- record = (yield service.recordWithUID("__null__"))
+ record = (yield service.recordWithUID(u"__null__"))
self.assertEquals(record, None)
- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(record.uid, "__wsanchez__")
+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(record.uid, u"__wsanchez__")
@inlineCallbacks
def test_recordWithGUID(self):
service = self.service()
- record = (yield service.recordWithGUID("6C495FCD-7E78-4D5C-AA66-BC890AD04C9D"))
+ record = (
+ yield service.recordWithGUID(
+ UUID("6C495FCD-7E78-4D5C-AA66-BC890AD04C9D")
+ )
+ )
self.assertEquals(record, None)
@inlineCallbacks
@@ -80,27 +81,33 @@
records = (yield service.recordsWithRecordType(object()))
self.assertEquals(set(records), set())
- records = (yield service.recordsWithRecordType(service.recordType.user))
- self.assertRecords(records,
+ records = (
+ yield service.recordsWithRecordType(service.recordType.user)
+ )
+ self.assertRecords(
+ records,
(
- "__wsanchez__",
- "__glyph__",
- "__sagen__",
- "__cdaboo__",
- "__dre__",
- "__exarkun__",
- "__dreid__",
- "__alyssa__",
- "__joe__",
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__sagen__",
+ u"__cdaboo__",
+ u"__dre__",
+ u"__exarkun__",
+ u"__dreid__",
+ u"__alyssa__",
+ u"__joe__",
),
)
- records = (yield service.recordsWithRecordType(service.recordType.group))
- self.assertRecords(records,
+ records = (
+ yield service.recordsWithRecordType(service.recordType.group)
+ )
+ self.assertRecords(
+ records,
(
- "__calendar-dev__",
- "__twisted__",
- "__developers__",
+ u"__calendar-dev__",
+ u"__twisted__",
+ u"__developers__",
),
)
@@ -109,42 +116,86 @@
def test_recordWithShortName(self):
service = self.service()
- record = (yield service.recordWithShortName(service.recordType.user, "null"))
+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"null",
+ )
+ )
self.assertEquals(record, None)
- record = (yield service.recordWithShortName(service.recordType.user, "wsanchez"))
- self.assertEquals(record.uid, "__wsanchez__")
+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"wsanchez",
+ )
+ )
+ self.assertEquals(record.uid, u"__wsanchez__")
- record = (yield service.recordWithShortName(service.recordType.user, "wilfredo_sanchez"))
- self.assertEquals(record.uid, "__wsanchez__")
+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"wilfredo_sanchez",
+ )
+ )
+ self.assertEquals(record.uid, u"__wsanchez__")
@inlineCallbacks
def test_recordsWithEmailAddress(self):
service = self.service()
- records = (yield service.recordsWithEmailAddress("wsanchez at bitbucket.calendarserver.org"))
- self.assertRecords(records, ("__wsanchez__",))
+ records = (
+ yield service.recordsWithEmailAddress(
+ u"wsanchez at bitbucket.calendarserver.org"
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
- records = (yield service.recordsWithEmailAddress("wsanchez at devnull.twistedmatrix.com"))
- self.assertRecords(records, ("__wsanchez__",))
+ records = (
+ yield service.recordsWithEmailAddress(
+ u"wsanchez at devnull.twistedmatrix.com"
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
- records = (yield service.recordsWithEmailAddress("shared at example.com"))
- self.assertRecords(records, ("__sagen__", "__dre__"))
+ records = (
+ yield service.recordsWithEmailAddress(
+ u"shared at example.com"
+ )
+ )
+ self.assertRecords(records, (u"__sagen__", u"__dre__"))
-class DirectoryServiceRealmTest(BaseTest):
+class DirectoryServiceTest(
+ unittest.TestCase,
+ DirectoryServiceConvenienceTestMixIn,
+ test_index.BaseDirectoryServiceTest,
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ def test_repr(self):
+ service = self.service()
+
+ self.assertEquals(repr(service), "<TestService (not loaded)>")
+ service.loadRecords()
+ self.assertEquals(repr(service), "<TestService u'xyzzy'>")
+
+
+
+class DirectoryServiceRealmTest(unittest.TestCase, BaseTest):
def test_realmNameImmutable(self):
def setRealmName():
service = self.service()
- service.realmName = "foo"
+ service.realmName = u"foo"
self.assertRaises(AssertionError, setRealmName)
-class DirectoryServiceParsingTest(BaseTest):
+class DirectoryServiceParsingTest(unittest.TestCase, BaseTest):
def test_reloadInterval(self):
service = self.service()
@@ -176,12 +227,13 @@
def test_badRootElement(self):
- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
-<frobnitz />
-"""
- )
+ <frobnitz />
+ """[1:]
+ )))
self.assertRaises(ParseError, service.loadRecords)
try:
@@ -189,16 +241,17 @@
except ParseError as e:
self.assertTrue(str(e).startswith("Incorrect root element"), e)
else:
- raise AssertionError
+ raise AssertionError("Expected ParseError")
def test_noRealmName(self):
- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
-<directory />
-"""
- )
+ <directory />
+ """[1:]
+ )))
self.assertRaises(ParseError, service.loadRecords)
try:
@@ -206,7 +259,7 @@
except ParseError as e:
self.assertTrue(str(e).startswith("No realm name"), e)
else:
- raise AssertionError
+ raise AssertionError("Expected ParseError")
def test_unknownFieldElementsClean(self):
@@ -215,19 +268,23 @@
def test_unknownFieldElementsDirty(self):
- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
-<directory realm="Unknown Record Types">
- <record type="user">
- <uid>__wsanchez__</uid>
- <short-name>wsanchez</short-name>
- <political-affiliation>Community and Freedom Party</political-affiliation>
- </record>
-</directory>
-"""
+ <directory realm="Unknown Record Types">
+ <record type="user">
+ <uid>__wsanchez__</uid>
+ <short-name>wsanchez</short-name>
+ <political-affiliation>Community and Freedom Party</political-affiliation>
+ </record>
+ </directory>
+ """[1:]
+ )))
+ self.assertEquals(
+ set(service.unknownFieldElements),
+ set((u"political-affiliation",))
)
- self.assertEquals(set(service.unknownFieldElements), set(("political-affiliation",)))
def test_unknownRecordTypesClean(self):
@@ -236,34 +293,37 @@
def test_unknownRecordTypesDirty(self):
- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
-<directory realm="Unknown Record Types">
- <record type="camera">
- <uid>__d600__</uid>
- <short-name>d600</short-name>
- <full-name>Nikon D600</full-name>
- </record>
-</directory>
-"""
- )
- self.assertEquals(set(service.unknownRecordTypes), set(("camera",)))
+ <directory realm="Unknown Record Types">
+ <record type="camera">
+ <uid>__d600__</uid>
+ <short-name>d600</short-name>
+ <full-name>Nikon D600</full-name>
+ </record>
+ </directory>
+ """[1:]
+ )))
+ self.assertEquals(set(service.unknownRecordTypes), set((u"camera",)))
-class DirectoryServiceQueryTest(BaseTest):
+class DirectoryServiceQueryTest(unittest.TestCase, BaseTest):
@inlineCallbacks
def test_queryAnd(self):
service = self.service()
- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared at example.com"),
- service.query("shortNames", "sagen"),
- ),
- operand=Operand.AND
+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared at example.com"),
+ service.query(u"shortNames", u"sagen"),
+ ),
+ operand=Operand.AND
+ )
)
- self.assertRecords(records, ("__sagen__",))
+ self.assertRecords(records, (u"__sagen__",))
@inlineCallbacks
@@ -272,12 +332,14 @@
Test optimized case, where first expression yields no results.
"""
service = self.service()
- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "nobody at example.com"),
- service.query("shortNames", "sagen"),
- ),
- operand=Operand.AND
+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"nobody at example.com"),
+ service.query(u"shortNames", u"sagen"),
+ ),
+ operand=Operand.AND
+ )
)
self.assertRecords(records, ())
@@ -285,102 +347,131 @@
@inlineCallbacks
def test_queryOr(self):
service = self.service()
- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared at example.com"),
- service.query("shortNames", "wsanchez"),
- ),
- operand=Operand.OR
+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared at example.com"),
+ service.query(u"shortNames", u"wsanchez"),
+ ),
+ operand=Operand.OR
+ )
)
- self.assertRecords(records, ("__sagen__", "__dre__", "__wsanchez__"))
+ self.assertRecords(
+ records,
+ (u"__sagen__", u"__dre__", u"__wsanchez__")
+ )
@inlineCallbacks
def test_queryNot(self):
service = self.service()
- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared at example.com"),
- service.query("shortNames", "sagen", flags=MatchFlags.NOT),
- ),
- operand=Operand.AND
+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(
+ u"emailAddresses", u"shared at example.com"
+ ),
+ service.query(
+ u"shortNames", u"sagen",
+ flags=MatchFlags.NOT
+ ),
+ ),
+ operand=Operand.AND
+ )
)
- self.assertRecords(records, ("__dre__",))
+ self.assertRecords(records, (u"__dre__",))
@inlineCallbacks
def test_queryNotNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared at example.com"),
- service.query("fullNames", "Andre LaBranche", flags=MatchFlags.NOT),
- ),
- operand=Operand.AND
+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared at example.com"),
+ service.query(
+ u"fullNames", u"Andre LaBranche",
+ flags=MatchFlags.NOT
+ ),
+ ),
+ operand=Operand.AND
+ )
)
- self.assertRecords(records, ("__sagen__",))
+ self.assertRecords(records, (u"__sagen__",))
@inlineCallbacks
def test_queryCaseInsensitive(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("shortNames", "SagEn", flags=MatchFlags.caseInsensitive),
- ))
- self.assertRecords(records, ("__sagen__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"SagEn",
+ flags=MatchFlags.caseInsensitive
+ )
+ )
+ self.assertRecords(records, (u"__sagen__",))
@inlineCallbacks
def test_queryCaseInsensitiveNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("fullNames", "moRGen SAGen", flags=MatchFlags.caseInsensitive),
- ))
- self.assertRecords(records, ("__sagen__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"moRGen SAGen",
+ flags=MatchFlags.caseInsensitive
+ )
+ )
+ self.assertRecords(records, (u"__sagen__",))
@inlineCallbacks
def test_queryStartsWith(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("shortNames", "wil", matchType=MatchType.startsWith),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"wil",
+ matchType=MatchType.startsWith
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryStartsWithNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("fullNames", "Wilfredo", matchType=MatchType.startsWith),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"Wilfredo",
+ matchType=MatchType.startsWith
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryStartsWithNot(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "shortNames", "w",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
+ u"shortNames", u"w",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
self.assertRecords(
records,
(
- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
),
)
@@ -394,28 +485,28 @@
should NOT require that all match?
"""
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "shortNames", "wil",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
+ u"shortNames", u"wil",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
self.assertRecords(
records,
(
- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
- '__wsanchez__',
+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
+ u"__wsanchez__",
),
)
@@ -423,27 +514,27 @@
@inlineCallbacks
def test_queryStartsWithNotNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "fullNames", "Wilfredo",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
+ u"fullNames", u"Wilfredo",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
self.assertRecords(
records,
(
- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
),
)
@@ -451,71 +542,77 @@
@inlineCallbacks
def test_queryStartsWithCaseInsensitive(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "shortNames", "WIL",
- matchType = MatchType.startsWith,
- flags = MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ u"shortNames", u"WIL",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.caseInsensitive,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryStartsWithCaseInsensitiveNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "fullNames", "wilfrEdo",
- matchType = MatchType.startsWith,
- flags = MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ u"fullNames", u"wilfrEdo",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.caseInsensitive,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryContains(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("shortNames", "sanchez", matchType=MatchType.contains),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"sanchez",
+ matchType=MatchType.contains,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryContainsNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
- service.query("fullNames", "fred", matchType=MatchType.contains),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"fred",
+ matchType=MatchType.contains,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryContainsNot(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "shortNames", "sanchez",
- matchType = MatchType.contains,
- flags = MatchFlags.NOT,
- ),
- ))
+ u"shortNames", u"sanchez",
+ matchType=MatchType.contains,
+ flags=MatchFlags.NOT,
+ )
+ )
self.assertRecords(
records,
(
- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
),
)
@@ -523,27 +620,27 @@
@inlineCallbacks
def test_queryContainsNotNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "fullNames", "fred",
- matchType = MatchType.contains,
- flags = MatchFlags.NOT,
- ),
- ))
+ u"fullNames", u"fred",
+ matchType=MatchType.contains,
+ flags=MatchFlags.NOT,
+ )
+ )
self.assertRecords(
records,
(
- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
),
)
@@ -551,51 +648,57 @@
@inlineCallbacks
def test_queryContainsCaseInsensitive(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "shortNames", "Sanchez",
+ u"shortNames", u"Sanchez",
matchType=MatchType.contains,
flags=MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
@inlineCallbacks
def test_queryContainsCaseInsensitiveNoIndex(self):
service = self.service()
- records = yield service.recordsFromQuery((
+ records = yield service.recordsFromExpression(
service.query(
- "fullNames", "frEdo",
+ u"fullNames", u"frEdo",
matchType=MatchType.contains,
flags=MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
-class DirectoryServiceMutableTest(BaseTest):
+class DirectoryServiceMutableTest(unittest.TestCase, BaseTest):
@inlineCallbacks
def test_updateRecord(self):
service = self.service()
- record = (yield service.recordWithUID("__wsanchez__"))
+ record = (yield service.recordWithUID(u"__wsanchez__"))
fields = record.fields.copy()
- fields[service.fieldName.fullNames] = ["Wilfredo Sanchez Vega"]
+ fields[service.fieldName.fullNames] = [u"Wilfredo Sanchez Vega"]
updatedRecord = DirectoryRecord(service, fields)
yield service.updateRecords((updatedRecord,))
# Verify change is present immediately
- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(set(record.fullNames), set(("Wilfredo Sanchez Vega",)))
+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(
+ set(record.fullNames),
+ set((u"Wilfredo Sanchez Vega",))
+ )
# Verify change is persisted
service.flush()
- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(set(record.fullNames), set(("Wilfredo Sanchez Vega",)))
+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(
+ set(record.fullNames),
+ set((u"Wilfredo Sanchez Vega",))
+ )
@inlineCallbacks
@@ -604,23 +707,23 @@
newRecord = DirectoryRecord(
service,
- fields = {
- service.fieldName.uid: "__plugh__",
+ fields={
+ service.fieldName.uid: u"__plugh__",
service.fieldName.recordType: service.recordType.user,
- service.fieldName.shortNames: ("plugh",),
+ service.fieldName.shortNames: (u"plugh",),
}
)
yield service.updateRecords((newRecord,), create=True)
# Verify change is present immediately
- record = (yield service.recordWithUID("__plugh__"))
- self.assertEquals(set(record.shortNames), set(("plugh",)))
+ record = (yield service.recordWithUID(u"__plugh__"))
+ self.assertEquals(set(record.shortNames), set((u"plugh",)))
# Verify change is persisted
service.flush()
- record = (yield service.recordWithUID("__plugh__"))
- self.assertEquals(set(record.shortNames), set(("plugh",)))
+ record = (yield service.recordWithUID(u"__plugh__"))
+ self.assertEquals(set(record.shortNames), set((u"plugh",)))
def test_addRecordNoCreate(self):
@@ -628,81 +731,91 @@
newRecord = DirectoryRecord(
service,
- fields = {
- service.fieldName.uid: "__plugh__",
+ fields={
+ service.fieldName.uid: u"__plugh__",
service.fieldName.recordType: service.recordType.user,
- service.fieldName.shortNames: ("plugh",),
+ service.fieldName.shortNames: (u"plugh",),
}
)
- self.assertFailure(service.updateRecords((newRecord,)), NoSuchRecordError)
+ self.assertFailure(
+ service.updateRecords((newRecord,)),
+ NoSuchRecordError
+ )
@inlineCallbacks
def test_removeRecord(self):
service = self.service()
- yield service.removeRecords(("__wsanchez__",))
+ yield service.removeRecords((u"__wsanchez__",))
# Verify change is present immediately
- self.assertEquals((yield service.recordWithUID("__wsanchez__")), None)
+ self.assertEquals((yield service.recordWithUID(u"__wsanchez__")), None)
# Verify change is persisted
service.flush()
- self.assertEquals((yield service.recordWithUID("__wsanchez__")), None)
+ self.assertEquals((yield service.recordWithUID(u"__wsanchez__")), None)
def test_removeRecordNoExist(self):
service = self.service()
- return service.removeRecords(("__plugh__",))
+ return service.removeRecords((u"__plugh__",))
-class DirectoryRecordTest(BaseTest, test_directory.DirectoryRecordTest):
+class DirectoryRecordTest(
+ unittest.TestCase,
+ BaseTest,
+ test_index.BaseDirectoryRecordTest
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
@inlineCallbacks
- def test_members(self):
+ def test_members_group(self):
service = self.service()
- record = (yield service.recordWithUID("__wsanchez__"))
+ record = (yield service.recordWithUID(u"__wsanchez__"))
members = (yield record.members())
self.assertEquals(set(members), set())
- record = (yield service.recordWithUID("__twisted__"))
+ record = (yield service.recordWithUID(u"__twisted__"))
members = (yield record.members())
self.assertEquals(
set((member.uid for member in members)),
set((
- "__wsanchez__",
- "__glyph__",
- "__exarkun__",
- "__dreid__",
- "__dre__",
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__exarkun__",
+ u"__dreid__",
+ u"__dre__",
))
)
- record = (yield service.recordWithUID("__developers__"))
+ record = (yield service.recordWithUID(u"__developers__"))
members = (yield record.members())
self.assertEquals(
set((member.uid for member in members)),
set((
- "__calendar-dev__",
- "__twisted__",
- "__alyssa__",
+ u"__calendar-dev__",
+ u"__twisted__",
+ u"__alyssa__",
))
)
@inlineCallbacks
- def test_groups(self):
+ def test_memberships(self):
service = self.service()
- record = (yield service.recordWithUID("__wsanchez__"))
+ record = (yield service.recordWithUID(u"__wsanchez__"))
groups = (yield record.groups())
self.assertEquals(
set(group.uid for group in groups),
set((
- "__calendar-dev__",
- "__twisted__",
+ u"__calendar-dev__",
+ u"__twisted__",
))
)
@@ -714,8 +827,8 @@
assert name is not None
return MatchExpression(
name, value,
- matchType = matchType,
- flags = flags,
+ matchType=matchType,
+ flags=flags,
)
@@ -735,11 +848,17 @@
filePath = FilePath(tmp)
filePath.setContent(xmlData)
- return serviceClass(filePath)
+ try:
+ return serviceClass(filePath)
+ except Exception as e:
+ raise AssertionError(
+ "Unable to instantiate XML service {0}: {1}"
+ .format(serviceClass, e)
+ )
-testXMLConfig = """<?xml version="1.0" encoding="utf-8"?>
+testXMLConfig = b"""<?xml version="1.0" encoding="utf-8"?>
<directory realm="xyzzy">
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -45,15 +45,18 @@
self._constants = myConstants
+
def __getattr__(self, name):
try:
return self._constants[name]
except KeyError:
raise AttributeError(name)
+
def iterconstants(self):
return self._constants.itervalues()
+
def lookupByName(self, name):
try:
return self._constants[name]
@@ -61,6 +64,7 @@
raise ValueError(name)
+
def uniqueResult(values):
result = None
for value in values:
@@ -73,6 +77,7 @@
return result
+
def describe(constant):
if isinstance(constant, FlagConstant):
parts = []
@@ -83,6 +88,7 @@
return getattr(constant, "description", constant.name)
+
def iterFlags(flags):
if hasattr(flags, "__iter__"):
return flags
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -28,6 +28,7 @@
]
from time import time
+from uuid import UUID
from xml.etree.ElementTree import parse as parseXML
from xml.etree.ElementTree import ParseError as XMLParseError
@@ -62,38 +63,38 @@
##
class Element(Values):
- directory = ValueConstant("directory")
- record = ValueConstant("record")
+ directory = ValueConstant(u"directory")
+ record = ValueConstant(u"record")
#
# Field names
#
- uid = ValueConstant("uid")
+ uid = ValueConstant(u"uid")
uid.fieldName = BaseFieldName.uid
- guid = ValueConstant("guid")
+ guid = ValueConstant(u"guid")
guid.fieldName = BaseFieldName.guid
- shortName = ValueConstant("short-name")
+ shortName = ValueConstant(u"short-name")
shortName.fieldName = BaseFieldName.shortNames
- fullName = ValueConstant("full-name")
+ fullName = ValueConstant(u"full-name")
fullName.fieldName = BaseFieldName.fullNames
- emailAddress = ValueConstant("email")
+ emailAddress = ValueConstant(u"email")
emailAddress.fieldName = BaseFieldName.emailAddresses
- password = ValueConstant("password")
+ password = ValueConstant(u"password")
password.fieldName = BaseFieldName.password
- memberUID = ValueConstant("member-uid")
+ memberUID = ValueConstant(u"member-uid")
memberUID.fieldName = IndexFieldName.memberUIDs
class Attribute(Values):
- realm = ValueConstant("realm")
- recordType = ValueConstant("type")
+ realm = ValueConstant(u"realm")
+ recordType = ValueConstant(u"type")
@@ -101,16 +102,16 @@
#
# Booleans
#
- true = ValueConstant("true")
- false = ValueConstant("false")
+ true = ValueConstant(u"true")
+ false = ValueConstant(u"false")
#
# Record types
#
- user = ValueConstant("user")
+ user = ValueConstant(u"user")
user.recordType = RecordType.user
- group = ValueConstant("group")
+ group = ValueConstant(u"group")
group.recordType = RecordType.group
@@ -234,14 +235,14 @@
"Incorrect root element: {0}".format(directoryNode.tag)
)
- realmName = directoryNode.get(
- self.attribute.realm.value, ""
- ).encode("utf-8")
+ realmName = unicode(directoryNode.get(
+ self.attribute.realm.value, u""
+ ))
if not realmName:
raise ParseError("No realm name.")
- unknownRecordTypes = set()
+ unknownRecordTypes = set()
unknownFieldElements = set()
records = set()
@@ -258,39 +259,24 @@
# Store results
#
- index = {}
+ self.flush()
+ self.indexRecords(records)
- for fieldName in self.indexedFields:
- index[fieldName] = {}
-
- for record in records:
- for fieldName in self.indexedFields:
- values = record.fields.get(fieldName, None)
-
- if values is not None:
- if not BaseFieldName.isMultiValue(fieldName):
- values = (values,)
-
- for value in values:
- index[fieldName].setdefault(value, set()).add(record)
-
self._realmName = realmName
- self._unknownRecordTypes = unknownRecordTypes
+ self._unknownRecordTypes = unknownRecordTypes
self._unknownFieldElements = unknownFieldElements
self._cacheTag = cacheTag
self._lastRefresh = now
- self.index = index
-
return etree
def parseRecordNode(self, recordNode, unknownFieldElements=None):
recordTypeAttribute = recordNode.get(
- self.attribute.recordType.value, ""
- ).encode("utf-8")
+ self.attribute.recordType.value, u""
+ )
if recordTypeAttribute:
try:
recordType = (
@@ -317,8 +303,16 @@
if unknownFieldElements is not None:
unknownFieldElements.add(fieldNode.tag)
- value = fieldNode.text.encode("utf-8")
+ vType = BaseFieldName.valueType(fieldName)
+ if vType in (unicode, UUID):
+ value = vType(fieldNode.text)
+ else:
+ raise AssertionError(
+ "Unknown value type {0} for field {1}",
+ vType, fieldName
+ )
+
if BaseFieldName.isMultiValue(fieldName):
values = fields.setdefault(fieldName, [])
values.append(value)
@@ -339,11 +333,11 @@
def flush(self):
BaseDirectoryService.flush(self)
- self._realmName = None
- self._unknownRecordTypes = None
+ self._realmName = None
+ self._unknownRecordTypes = None
self._unknownFieldElements = None
- self._cacheTag = None
- self._lastRefresh = 0
+ self._cacheTag = None
+ self._lastRefresh = 0
def updateRecords(self, records, create=False):
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py (from rev 12016, CalendarServer/trunk/twisted/plugins/masterchild.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,58 @@
+##
+# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from zope.interface import implementer
+
+from twisted.python.reflect import namedClass
+from twisted.plugin import IPlugin
+from twisted.application.service import IServiceMaker
+
+from twext.application.masterchild import MasterOptions, ChildOptions
+
+
+ at implementer(IPlugin, IServiceMaker)
+class ServiceMaker(object):
+ def __init__(self, name, description, options, serviceMakerClass):
+ self.tapname = name
+ self.description = description
+ self.options = options
+ self.serviceMakerClass = serviceMakerClass
+ self._serviceMaker = None
+
+
+ def makeService(self, options):
+ if self._serviceMaker is None:
+ self._serviceMaker = namedClass(self.serviceMakerClass)()
+
+ return self._serviceMaker.makeService(options)
+
+
+
+masterServiceMaker = ServiceMaker(
+ "master",
+ "Master process application container",
+ MasterOptions,
+ "twext.application.masterchild.MasterServiceMaker"
+)
+
+
+
+childServiceMaker = ServiceMaker(
+ "child",
+ "Child process application container",
+ ChildOptions,
+ "twext.application.masterchild.ChildServiceMaker"
+)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -59,12 +59,14 @@
})
# Do some PyCalendar init
-from pycalendar.calendar import PyCalendar
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.icalendar.property import Property
+from pycalendar.vcard.card import Card
+from pycalendar.value import Value
-PyCalendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
+Calendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
+Card.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
# These are properties we use directly and we want the default value type set for TEXT
-PyCalendarProperty.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", PyCalendarValue.VALUETYPE_TEXT)
-PyCalendarProperty.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", PyCalendarValue.VALUETYPE_TEXT)
+Property.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", Value.VALUETYPE_TEXT)
+Property.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", Value.VALUETYPE_TEXT)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,6 +42,8 @@
accountingEnabledForPrincipal(principal)
)
+
+
def accountingEnabledForCategory(category):
"""
Determine if accounting is enabled for the given category.
@@ -51,6 +53,8 @@
return False
return AccountingCategories.get(category, False)
+
+
def accountingEnabledForPrincipal(principal):
"""
Determine if accounting is enabled for the given principal.
@@ -69,6 +73,8 @@
return False
+
+
def emitAccounting(category, principal, data, tag=None):
"""
Write the supplied data to the appropriate location for the given
@@ -80,7 +86,7 @@
@type category: C{tuple}
@param data: data to write.
@type data: C{str}
- """
+ """
if isinstance(principal, str):
principalLogPath = principal
elif accountingEnabled(category, principal):
@@ -107,7 +113,7 @@
logDirectory,
datetime.datetime.now().isoformat()
)
-
+
if not os.path.isdir(os.path.join(logRoot, logDirectory)):
os.makedirs(os.path.join(logRoot, logDirectory))
logFilename = "%s-01" % (logFilename,)
@@ -128,7 +134,7 @@
log.error("Too many %s accounting files for %s" % (category, principal))
return None
index += 1
-
+
#
# Now write out the data to the log file
#
@@ -137,7 +143,7 @@
logFile.write(data)
finally:
logFile.close()
-
+
return logFilename
except OSError, e:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -22,7 +22,7 @@
1. An alternative to password based BASIC authentication in which the BASIC credentials are
verified against Kerberos.
-
+
2. The NEGOTIATE mechanism (as defined in http://www.ietf.org/rfc/rfc4559.txt)
that implements full GSSAPI authentication.
"""
@@ -58,7 +58,7 @@
def __init__(self, principal=None, type=None, hostname=None):
"""
-
+
@param principal: full Kerberos principal (e.g., 'HTTP/server.example.com at EXAMPLE.COM'). If C{None}
then the type and hostname arguments are used instead.
@type service: str
@@ -82,6 +82,7 @@
self.service, self.realm = self._splitPrincipal(principal)
+
def _splitPrincipal(self, principal):
try:
@@ -93,12 +94,14 @@
except IndexError:
self.log.error("Invalid Kerberos principal: %s" % (principal,))
raise ValueError('Authentication System Failure: Invalid Kerberos principal: %s' % (principal,))
-
+
service = "%s@%s" % (servicetype, service,)
realm = realm
-
+
return (service, realm,)
-
+
+
+
class BasicKerberosCredentials(credentials.UsernamePassword):
"""
A set of user/password credentials that checks itself against Kerberos.
@@ -106,7 +109,7 @@
def __init__(self, username, password, service, realm):
"""
-
+
@param username: user name of user to authenticate
@type username: str
@param password: password for user being authenticated
@@ -117,11 +120,13 @@
@type hostname: str
"""
credentials.UsernamePassword.__init__(self, username, password)
-
+
# Convert Kerberos principal spec into service and realm
self.service = service
self.default_realm = realm
-
+
+
+
class BasicKerberosCredentialFactory(KerberosCredentialFactoryBase):
"""
Authorizer for insecure Basic (base64-encoded plaintext) authentication.
@@ -134,7 +139,7 @@
def __init__(self, principal=None, type=None, hostname=None):
"""
-
+
@param principal: full Kerberos principal (e.g., 'HTTP/server.example.com at EXAMPLE.COM'). If C{None}
then the type and hostname arguments are used instead.
@type service: str
@@ -146,9 +151,11 @@
super(BasicKerberosCredentialFactory, self).__init__(principal, type, hostname)
+
def getChallenge(self, _ignore_peer):
return succeed({'realm': self.realm})
+
def decode(self, response, request): #@UnusedVariable
try:
creds = (response + '===').decode('base64')
@@ -161,6 +168,8 @@
return succeed(c)
raise error.LoginFailed('Invalid credentials')
+
+
class BasicKerberosCredentialsChecker(object):
log = Logger()
@@ -187,9 +196,11 @@
pcreds.authnPrincipal,
pcreds.authzPrincipal,
))
-
+
raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.authnURI,))
+
+
class NegotiateCredentials(object):
"""
A set of user/password credentials that checks itself against Kerberos.
@@ -198,10 +209,12 @@
implements(credentials.ICredentials)
def __init__(self, principal, username):
-
+
self.principal = principal
self.username = username
-
+
+
+
class NegotiateCredentialFactory(KerberosCredentialFactoryBase):
"""
Authorizer for Negotiate authentication (http://www.ietf.org/rfc/rfc4559.txt).
@@ -211,7 +224,7 @@
def __init__(self, principal=None, type=None, hostname=None):
"""
-
+
@param principal: full Kerberos principal (e.g., 'HTTP/server.example.com at EXAMPLE.COM'). If C{None}
then the type and hostname arguments are used instead.
@type service: str
@@ -223,22 +236,24 @@
super(NegotiateCredentialFactory, self).__init__(principal, type, hostname)
+
def getChallenge(self, _ignore_peer):
return succeed({})
+
def decode(self, base64data, request):
-
+
# Init GSSAPI first - we won't specify the service now as we need to accept a target
# name that is case-insenstive as some clients will use "http" instead of "HTTP"
try:
- _ignore_result, context = kerberos.authGSSServerInit("");
+ _ignore_result, context = kerberos.authGSSServerInit("")
except kerberos.GSSError, ex:
self.log.error("authGSSServerInit: %s(%s)" % (ex[0][0], ex[1][0],))
raise error.LoginFailed('Authentication System Failure: %s(%s)' % (ex[0][0], ex[1][0],))
# Do the GSSAPI step and get response and username
try:
- kerberos.authGSSServerStep(context, base64data);
+ kerberos.authGSSServerStep(context, base64data)
except kerberos.GSSError, ex:
self.log.error("authGSSServerStep: %s(%s)" % (ex[0][0], ex[1][0],))
kerberos.authGSSServerClean(context)
@@ -264,13 +279,13 @@
principal = kerberos.authGSSServerUserName(context)
username = principal
realmname = ""
-
+
# Username may include realm suffix which we want to strip
if username.find("@") != -1:
splits = username.split("@", 1)
username = splits[0]
realmname = splits[1]
-
+
# We currently do not support cross-realm authentication, so we
# must verify that the realm we got exactly matches the one we expect.
if realmname != self.realm:
@@ -278,11 +293,11 @@
# Close the context
try:
- kerberos.authGSSServerClean(context);
+ kerberos.authGSSServerClean(context)
except kerberos.GSSError, ex:
self.log.error("authGSSServerClean: %s" % (ex[0][0], ex[1][0],))
raise error.LoginFailed('Authentication System Failure %s(%s)' % (ex[0][0], ex[1][0],))
-
+
# If we successfully decoded and verified the Kerberos credentials we need to add the Kerberos
# response data to the outgoing request
@@ -299,6 +314,8 @@
return succeed(NegotiateCredentials(principal, username))
+
+
class NegotiateCredentialsChecker(object):
implements(checkers.ICredentialsChecker)
@@ -320,6 +337,5 @@
pcreds.authnPrincipal,
pcreds.authzPrincipal,
))
-
+
raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.authnURI,))
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -38,19 +38,22 @@
for x in xrange(0, len(argv)):
opt = argv[x]
if opt.startswith('-'):
- self[opt.strip('-')] = argv[x+1]
+ self[opt.strip('-')] = argv[x + 1]
+
def debug(string):
if VERBOSE:
print("DEBUG:", string)
+
def funclog(string):
if FUNCLOG:
print("FUNCLOG:", string)
+
def logFuncCall(func):
def printArgs(args):
a = []
@@ -60,6 +63,7 @@
return ''.join(a).strip(', ')
+
def printKwargs(kwargs):
a = []
for kwarg, value in kwargs:
@@ -67,8 +71,9 @@
return ''.join(a).strip(', ')
+
def _(*args, **kwargs):
- funclog("%s(%s)" % (func.func_name,
+ funclog("%s(%s)" % (func.func_name,
', '.join((printArgs(args),
printKwargs(kwargs))).strip(', ')))
@@ -77,10 +82,11 @@
funclog("%s - > %s" % (func.func_name, retval))
return retval
-
+
return _
+
@logFuncCall
def readConfig(configFile):
config = readPlist(configFile + '.default')
@@ -89,14 +95,16 @@
config.update(readPlist(configFile))
return config
-
+
+
@logFuncCall
def mkroot(path):
root = '/'.join(path.rstrip('/').split('/')[:-1])
os.makedirs(root)
+
@logFuncCall
def serveradmin(action, service):
cmd = ' '.join((
@@ -112,6 +120,7 @@
return status
+
@logFuncCall
def isRunning(service):
cmd = ' '.join((
@@ -134,21 +143,25 @@
return False
+
@logFuncCall
def copy(src, dst):
shutil.copytree(src, dst)
+
@logFuncCall
def move(src, dst):
os.rename(src, dst)
+
@logFuncCall
def remove(dst):
shutil.rmtree(dst)
+
@logFuncCall
def purge(root, patterns):
removed = []
@@ -175,5 +188,5 @@
os.remove(full)
removed.append(full)
-
+
return removed
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -25,8 +25,8 @@
See draft spec: http://ietf.webdav.org/caldav/draft-dusseault-caldav.txt
"""
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from txdav.xml.element import registerElement, dav_namespace
from txdav.xml.element import WebDAVElement, PCDATAElement
@@ -110,8 +110,8 @@
if "start" not in attributes and "end" not in attributes:
raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
- self.start = PyCalendarDateTime.parseText(attributes["start"]) if "start" in attributes else None
- self.end = PyCalendarDateTime.parseText(attributes["end"]) if "end" in attributes else None
+ self.start = DateTime.parseText(attributes["start"]) if "start" in attributes else None
+ self.end = DateTime.parseText(attributes["end"]) if "end" in attributes else None
def valid(self, level=0):
@@ -139,23 +139,102 @@
-class CalDAVTimeZoneElement (CalDAVTextElement):
+class CalDAVDataMixin(object):
"""
- CalDAV element containing iCalendar data with a single VTIMEZONE component.
+ A mixin to support accept/returning data in various formats.
"""
+
+ def __init__(self, *children, **attributes):
+
+ if "content-type" in attributes:
+ self.content_type = attributes["content-type"]
+ else:
+ self.content_type = "text/calendar"
+
+ if "version" in attributes:
+ self.version = attributes["version"]
+ else:
+ self.version = "2.0"
+
+ super(CalDAVDataMixin, self).__init__(*children, **attributes)
+
+
+ def verifyTypeVersion(self):
+ """
+ Make sure any content-type and version matches at least one supported set.
+
+ @return: C{True} if there is at least one match, C{False} otherwise.
+ """
+ allowedTypes = set()
+ allowedTypes.add(("text/calendar", "2.0",))
+ if config.EnableJSONData:
+ allowedTypes.add(("application/calendar+json", "2.0",))
+ for format, version in allowedTypes:
+ if (format == self.content_type) and (version == self.version):
+ return True
+
+ return False
+
+
+ @classmethod
+ def fromCalendar(clazz, calendar, format=None):
+ attrs = {}
+ if format is not None and format != "text/calendar":
+ attrs["content-type"] = format
+
+ if isinstance(calendar, str):
+ if not calendar:
+ raise ValueError("Missing calendar data")
+ return clazz(PCDATAElement(calendar), **attrs)
+ elif isinstance(calendar, iComponent):
+ assert calendar.name() == "VCALENDAR", "Not a calendar: %r" % (calendar,)
+ return clazz(PCDATAElement(calendar.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=format)), **attrs)
+ else:
+ raise ValueError("Not a calendar: %s" % (calendar,))
+
+ fromTextData = fromCalendar
+ fromComponent = fromCalendar
+
def calendar(self):
"""
- Returns a calendar component derived from this element, which contains
- exactly one VTIMEZONE component.
+ Returns a calendar component derived from this element.
"""
- return iComponent.fromString(str(self))
+ data = self.calendarData()
+ if data:
+ return iComponent.fromString(data, format=self.content_type)
+ else:
+ return None
+ generateComponent = calendar
+
+ def calendarData(self):
+ """
+ Returns the calendar data derived from this element.
+ """
+ for data in self.children:
+ if not isinstance(data, PCDATAElement):
+ return None
+ else:
+ # We guaranteed in __init__() that there is only one child...
+ break
+
+ return str(data)
+
+ textData = calendarData
+
+
+
+class CalDAVTimeZoneElement (CalDAVDataMixin, CalDAVTextElement):
+ """
+ CalDAV element containing iCalendar data with a single VTIMEZONE component.
+ """
+
def gettimezone(self):
"""
Get the timezone to use. If none, return UTC timezone.
- @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
+ @return: the L{Timezone} derived from the VTIMEZONE or utc.
"""
calendar = self.calendar()
if calendar is not None:
@@ -164,7 +243,7 @@
return tz
# Default to using utc tzinfo
- return PyCalendarTimezone(utc=True)
+ return Timezone(utc=True)
def valid(self):
@@ -231,8 +310,13 @@
name = "calendar-timezone"
hidden = True
+ allowed_attributes = {
+ "content-type": False,
+ "version" : False,
+ }
+
@registerElement
class SupportedCalendarComponentSets (CalDAVElement):
"""
@@ -428,7 +512,7 @@
@registerElement
-class CalendarData (CalDAVElement):
+class CalendarData (CalDAVDataMixin, CalDAVElement):
"""
Defines which parts of a calendar component object should be returned by a
report.
@@ -448,21 +532,6 @@
"version" : False,
}
- @classmethod
- def fromCalendar(clazz, calendar):
- if isinstance(calendar, str):
- if not calendar:
- raise ValueError("Missing calendar data")
- return clazz(PCDATAElement(calendar))
- elif isinstance(calendar, iComponent):
- assert calendar.name() == "VCALENDAR", "Not a calendar: %r" % (calendar,)
- return clazz(PCDATAElement(calendar.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference)))
- else:
- raise ValueError("Not a calendar: %s" % (calendar,))
-
- fromTextData = fromCalendar
-
-
def __init__(self, *children, **attributes):
super(CalendarData, self).__init__(*children, **attributes)
@@ -515,61 +584,8 @@
# optimize them originals away
self.children = (data,)
- if "content-type" in attributes:
- self.content_type = attributes["content-type"]
- else:
- self.content_type = "text/calendar"
- if "version" in attributes:
- self.version = attributes["version"]
- else:
- self.version = "2.0"
-
- def verifyTypeVersion(self, types_and_versions):
- """
- Make sure any content-type and version matches at least one of the supplied set.
-
- @param types_and_versions: a list of (content-type, version) tuples to test against.
- @return: True if there is at least one match, False otherwise.
- """
- for item in types_and_versions:
- if (item[0] == self.content_type) and (item[1] == self.version):
- return True
-
- return False
-
-
- def calendar(self):
- """
- Returns a calendar component derived from this element.
- """
- data = self.calendarData()
- if data:
- return iComponent.fromString(data)
- else:
- return None
-
- generateComponent = calendar
-
-
- def calendarData(self):
- """
- Returns the calendar data derived from this element.
- """
- for data in self.children:
- if not isinstance(data, PCDATAElement):
- return None
- else:
- # We guaranteed in __init__() that there is only one child...
- break
-
- return str(data)
-
- textData = calendarData
-
-
-
@registerElement
class CalendarComponent (CalDAVElement):
"""
@@ -882,8 +898,13 @@
"""
name = "timezone"
+ allowed_attributes = {
+ "content-type": False,
+ "version" : False,
+ }
+
@registerElement
class TimeRange (CalDAVTimeRangeElement):
"""
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -22,7 +22,7 @@
This API is considered private to static.py and is therefore subject to
change.
-See draft spec:
+See draft spec:
"""
from txdav.xml.element import registerElement, dav_namespace
@@ -30,6 +30,7 @@
from txdav.xml.element import WebDAVEmptyElement, WebDAVTextElement
from txdav.xml.element import ResourceType, Collection
+from twistedcaldav.config import config
from twistedcaldav.vcard import Component
##
@@ -50,6 +51,7 @@
namespace = carddav_namespace
+
class CardDAVEmptyElement (WebDAVEmptyElement):
"""
CardDAV element with no contents.
@@ -57,6 +59,7 @@
namespace = carddav_namespace
+
class CardDAVTextElement (WebDAVTextElement):
"""
CardDAV element containing PCDATA.
@@ -64,6 +67,93 @@
namespace = carddav_namespace
+
+class CardDAVDataMixin(object):
+ """
+ A mixin to support accept/returning data in various formats.
+ """
+
+ def __init__(self, *children, **attributes):
+
+ if "content-type" in attributes:
+ self.content_type = attributes["content-type"]
+ else:
+ self.content_type = "text/vcard"
+
+ if "version" in attributes:
+ self.version = attributes["version"]
+ else:
+ self.version = "3.0"
+
+ super(CardDAVDataMixin, self).__init__(*children, **attributes)
+
+
+ def verifyTypeVersion(self):
+ """
+ Make sure any content-type and version matches at least one supported set.
+
+ @return: C{True} if there is at least one match, C{False} otherwise.
+ """
+ allowedTypes = set()
+ allowedTypes.add(("text/vcard", "3.0",))
+ if config.EnableJSONData:
+ allowedTypes.add(("application/vcard+json", "3.0",))
+ for format, version in allowedTypes:
+ if (format == self.content_type) and (version == self.version):
+ return True
+
+ return False
+
+
+ @classmethod
+ def fromAddress(clazz, address, format=None):
+ attrs = {}
+ if format is not None and format != "text/vcard":
+ attrs["content-type"] = format
+
+ if isinstance(address, str):
+ if not address:
+ raise ValueError("Missing address data")
+ return clazz(PCDATAElement(address), **attrs)
+ elif isinstance(address, Component):
+ assert address.name() == "VCARD", "Not a vCard: %r" % (address,)
+ return clazz(PCDATAElement(address.getText(format)), **attrs)
+ else:
+ raise ValueError("Not an address: %s" % (address,))
+
+ fromTextData = fromAddress
+ fromComponent = fromAddress
+
+ def address(self):
+ """
+ Returns an address component derived from this element.
+ """
+ data = self.addressData()
+ if data:
+ return Component.fromString(data, format=self.content_type)
+ else:
+ return None
+
+ generateComponent = address
+
+
+ def addressData(self):
+ """
+ Returns the address data derived from this element.
+ """
+ for data in self.children:
+ if not isinstance(data, PCDATAElement):
+ return None
+ else:
+ # We guaranteed in __init__() that there is only one child...
+ break
+
+ return str(data)
+
+ textData = addressData
+
+
+
@registerElement
class AddressBookHomeSet (CardDAVElement):
"""
@@ -73,9 +163,10 @@
name = "addressbook-home-set"
hidden = True
- allowed_children = { (dav_namespace, "href"): (0, None) }
+ allowed_children = {(dav_namespace, "href"): (0, None)}
+
@registerElement
class AddressBookDescription (CardDAVTextElement):
"""
@@ -88,6 +179,7 @@
# May be protected; but we'll let the client set this if they like.
+
@registerElement
class SupportedAddressData (CardDAVElement):
"""
@@ -98,9 +190,10 @@
hidden = True
protected = True
- allowed_children = { (carddav_namespace, "address-data-type"): (0, None) }
+ allowed_children = {(carddav_namespace, "address-data-type"): (0, None)}
+
@registerElement
class MaxResourceSize (CardDAVTextElement):
"""
@@ -112,6 +205,7 @@
protected = True
+
@registerElement
class AddressBook (CardDAVEmptyElement):
"""
@@ -121,6 +215,7 @@
name = "addressbook"
+
@registerElement
class AddressBookQuery (CardDAVElement):
"""
@@ -130,11 +225,11 @@
name = "addressbook-query"
allowed_children = {
- (dav_namespace, "allprop" ): (0, None),
- (dav_namespace, "propname"): (0, None),
- (dav_namespace, "prop" ): (0, None),
- (carddav_namespace, "filter" ): (0, 1), # Actually (1, 1) unless element is empty
- (carddav_namespace, "limit" ): (0, None),
+ (dav_namespace, "allprop"): (0, None),
+ (dav_namespace, "propname"): (0, None),
+ (dav_namespace, "prop"): (0, None),
+ (carddav_namespace, "filter"): (0, 1), # Actually (1, 1) unless element is empty
+ (carddav_namespace, "limit"): (0, None),
}
def __init__(self, *children, **attributes):
@@ -148,9 +243,9 @@
qname = child.qname()
if qname in (
- (dav_namespace, "allprop" ),
+ (dav_namespace, "allprop"),
(dav_namespace, "propname"),
- (dav_namespace, "prop" ),
+ (dav_namespace, "prop"),
):
if props is not None:
raise ValueError("Only one of CardDAV:allprop, CardDAV:propname, CardDAV:prop allowed")
@@ -159,7 +254,7 @@
elif qname == (carddav_namespace, "filter"):
filter = child
elif qname == (carddav_namespace, "limit"):
- # type check
+ # type check
child.childOfType(NResults)
limit = child
@@ -170,11 +265,12 @@
if filter is None:
raise ValueError("CARDDAV:filter required")
- self.props = props
+ self.props = props
self.filter = filter
self.limit = limit
+
@registerElement
class AddressDataType (CardDAVEmptyElement):
"""
@@ -190,8 +286,9 @@
}
+
@registerElement
-class AddressData (CardDAVElement):
+class AddressData (CardDAVDataMixin, CardDAVElement):
"""
Defines which parts of a address component object should be returned by a
report.
@@ -201,7 +298,7 @@
allowed_children = {
(carddav_namespace, "allprop"): (0, 1),
- (carddav_namespace, "prop" ): (0, None),
+ (carddav_namespace, "prop"): (0, None),
PCDATAElement : (0, None),
}
allowed_attributes = {
@@ -209,27 +306,11 @@
"version" : False,
}
- @classmethod
- def fromAddress(clazz, address):
- assert address.name() == "VCARD", "Not a vCard: %r" % (address,)
- return clazz(PCDATAElement(str(address)))
-
- @classmethod
- def fromAddressData(clazz, addressdata):
- """
- Return a AddressData element comprised of the supplied address data.
- @param addressdata: a string of valid address data.
- @return: a L{Addressata} element.
- """
- return clazz(PCDATAElement(addressdata))
-
- fromTextData = fromAddressData
-
def __init__(self, *children, **attributes):
super(AddressData, self).__init__(*children, **attributes)
properties = None
- data = None
+ data = None
for child in self.children:
qname = child.qname()
@@ -256,73 +337,25 @@
else:
data += child
- else: raise AssertionError("We shouldn't be here")
+ else:
+ raise AssertionError("We shouldn't be here")
-
self.properties = properties
if data is not None:
try:
if properties is not None:
- raise ValueError("Only one of allprop, prop (%r) or PCDATA (%r) allowed"% (properties, str(data)))
+ raise ValueError("Only one of allprop, prop (%r) or PCDATA (%r) allowed" % (properties, str(data)))
except ValueError:
- if not data.isWhitespace(): raise
+ if not data.isWhitespace():
+ raise
else:
# Since we've already combined PCDATA elements, we'd may as well
# optimize them originals away
self.children = (data,)
- if "content-type" in attributes:
- self.content_type = attributes["content-type"]
- else:
- self.content_type = "text/vcard"
- if "version" in attributes:
- self.version = attributes["version"]
- else:
- self.version = "3.0"
- def verifyTypeVersion(self, types_and_versions):
- """
- Make sure any content-type and version matches at least one of the supplied set.
-
- @param types_and_versions: a list of (content-type, version) tuples to test against.
- @return: True if there is at least one match, False otherwise.
- """
- for item in types_and_versions:
- if (item[0] == self.content_type) and (item[1] == self.version):
- return True
-
- return False
-
- def address(self):
- """
- Returns an address component derived from this element.
- """
- data = self.addressData()
- if data:
- return Component.fromString(data)
- else:
- return None
-
- generateComponent = address
-
- def addressData(self):
- """
- Returns an address component derived from this element.
- """
- for data in self.children:
- if not isinstance(data, PCDATAElement):
- return None
- else:
- # We guaranteed in __init__() that there is only one child...
- break
-
- return str(data)
-
- textData = addressData
-
-
@registerElement
class AllProperties (CardDAVEmptyElement):
"""
@@ -332,6 +365,7 @@
name = "allprop"
+
@registerElement
class Property (CardDAVEmptyElement):
"""
@@ -362,6 +396,7 @@
self.novalue = False
+
@registerElement
class Filter (CardDAVElement):
"""
@@ -370,10 +405,11 @@
"""
name = "filter"
- allowed_children = { (carddav_namespace, "prop-filter"): (0, None) }
- allowed_attributes = { "test": False }
-
+ allowed_children = {(carddav_namespace, "prop-filter"): (0, None)}
+ allowed_attributes = {"test": False}
+
+
@registerElement
class PropertyFilter (CardDAVElement):
"""
@@ -383,9 +419,9 @@
name = "prop-filter"
allowed_children = {
- (carddav_namespace, "is-not-defined" ): (0, 1),
- (carddav_namespace, "text-match" ): (0, None),
- (carddav_namespace, "param-filter" ): (0, None),
+ (carddav_namespace, "is-not-defined"): (0, 1),
+ (carddav_namespace, "text-match"): (0, None),
+ (carddav_namespace, "param-filter"): (0, None),
}
allowed_attributes = {
"name": True,
@@ -393,6 +429,7 @@
}
+
@registerElement
class ParameterFilter (CardDAVElement):
"""
@@ -402,12 +439,13 @@
name = "param-filter"
allowed_children = {
- (carddav_namespace, "is-not-defined" ): (0, 1),
- (carddav_namespace, "text-match" ): (0, 1),
+ (carddav_namespace, "is-not-defined"): (0, 1),
+ (carddav_namespace, "text-match"): (0, 1),
}
- allowed_attributes = { "name": True }
+ allowed_attributes = {"name": True}
+
@registerElement
class Limit (WebDAVElement):
"""
@@ -416,10 +454,11 @@
namespace = carddav_namespace
name = "limit"
allowed_children = {
- (carddav_namespace, "nresults" ) : (1, 1),
+ (carddav_namespace, "nresults") : (1, 1),
}
+
@registerElement
class NResults (WebDAVTextElement):
"""
@@ -439,6 +478,7 @@
name = "is-not-defined"
+
@registerElement
class TextMatch (CardDAVTextElement):
"""
@@ -464,6 +504,7 @@
}
+
@registerElement
class AddressBookMultiGet (CardDAVElement):
"""
@@ -475,10 +516,10 @@
# To allow for an empty element in a supported-report-set property we need
# to relax the child restrictions
allowed_children = {
- (dav_namespace, "allprop" ): (0, 1),
+ (dav_namespace, "allprop"): (0, 1),
(dav_namespace, "propname"): (0, 1),
- (dav_namespace, "prop" ): (0, 1),
- (dav_namespace, "href" ): (0, None), # Actually ought to be (1, None)
+ (dav_namespace, "prop"): (0, 1),
+ (dav_namespace, "href"): (0, None), # Actually ought to be (1, None)
}
def __init__(self, *children, **attributes):
@@ -491,9 +532,9 @@
qname = child.qname()
if qname in (
- (dav_namespace, "allprop" ),
+ (dav_namespace, "allprop"),
(dav_namespace, "propname"),
- (dav_namespace, "prop" ),
+ (dav_namespace, "prop"),
):
if property is not None:
raise ValueError("Only one of DAV:allprop, DAV:propname, DAV:prop allowed")
@@ -502,10 +543,11 @@
elif qname == (dav_namespace, "href"):
resources.append(child)
- self.property = property
+ self.property = property
self.resources = resources
+
@registerElement
class NoUIDConflict(CardDAVElement):
"""
@@ -514,9 +556,10 @@
"""
name = "no-uid-conflict"
- allowed_children = { (dav_namespace, "href"): (1, 1) }
-
+ allowed_children = {(dav_namespace, "href"): (1, 1)}
+
+
@registerElement
class SupportedFilter(CardDAVElement):
"""
@@ -527,11 +570,12 @@
name = "supported-filter"
allowed_children = {
- (carddav_namespace, "prop-filter" ): (0, None),
+ (carddav_namespace, "prop-filter"): (0, None),
(carddav_namespace, "param-filter"): (0, None)
}
-
+
+
@registerElement
class DirectoryGateway(CardDAVElement):
"""
@@ -541,17 +585,19 @@
hidden = True
protected = True
- allowed_children = { (dav_namespace, "href"): (0, None) }
-
+ allowed_children = {(dav_namespace, "href"): (0, None)}
+
+
@registerElement
class Directory(CardDAVEmptyElement):
"""
CardDAV property on a principal to indicate where the directory resource is.
"""
name = "directory"
-
+
+
@registerElement
class DefaultAddressBookURL (CardDAVElement):
"""
@@ -559,14 +605,17 @@
"""
name = "default-addressbook-URL"
- allowed_children = { (dav_namespace, "href"): (0, 1) }
+ allowed_children = {(dav_namespace, "href"): (0, 1)}
+
+
##
# Extensions to ResourceType
##
-def _isAddressBook(self): return bool(self.childrenOfType(AddressBook))
+def _isAddressBook(self):
+ return bool(self.childrenOfType(AddressBook))
ResourceType.isAddressBook = _isAddressBook
ResourceType.addressbook = ResourceType(Collection(), AddressBook())
-ResourceType.directory = ResourceType(Collection(), AddressBook(), Directory())
+ResourceType.directory = ResourceType(Collection(), AddressBook(), Directory())
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -97,6 +97,9 @@
self._defaults = ConfigDict()
else:
self._defaults = ConfigDict(copy.deepcopy(defaults))
+ self.importedFiles = []
+ self.includedFiles = []
+ self.missingFiles = []
def getDefaults(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -34,7 +34,7 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.ical import Component as iComponent
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
calendarserver_namespace = "http://calendarserver.org/ns/"
@@ -640,7 +640,7 @@
def __init__(self, *children):
super(DTStamp, self).__init__(children)
- self.children = (PCDATAElement(PyCalendarDateTime.getNowUTC().getText()),)
+ self.children = (PCDATAElement(DateTime.getNowUTC().getText()),)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -44,14 +44,15 @@
"""
A ThreadPool that closes connections for each worker thread
"""
-
+
def _worker(self):
log.debug("Starting ADBAPI thread: %s" % (thread.get_ident(),))
ThreadPool._worker(self)
self._closeConnection()
+
def _closeConnection(self):
-
+
tid = thread.get_ident()
log.debug("Closing ADBAPI thread: %s" % (tid,))
@@ -59,6 +60,8 @@
self.pool._close(conn)
del self.pool.connections[tid]
+
+
class AbstractADBAPIDatabase(object):
"""
A generic SQL database.
@@ -66,7 +69,7 @@
def __init__(self, dbID, dbapiName, dbapiArgs, persistent, **kwargs):
"""
-
+
@param persistent: C{True} if the data in the DB must be perserved during upgrades,
C{False} if the DB data can be re-created from an external source.
@type persistent: bool
@@ -77,12 +80,14 @@
self.dbapikwargs = kwargs
self.persistent = persistent
-
+
self.initialized = False
+
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.pool)
+
@inlineCallbacks
def open(self):
"""
@@ -92,7 +97,7 @@
if not self.initialized:
self.pool = ConnectionPool(self.dbapiName, *self.dbapiArgs, **self.dbapikwargs)
-
+
# sqlite3 is not thread safe which means we have to close the sqlite3 connections in the same thread that
# opened them. We need a special thread pool class that has a thread worker function that does a close
# when a thread is closed.
@@ -126,7 +131,7 @@
elif version != self._db_version():
log.error("Database %s has different schema (v.%s vs. v.%s)"
% (self.dbID, version, self._db_version()))
-
+
# Upgrade the DB
yield self._db_upgrade(version)
@@ -139,8 +144,9 @@
self.pool = None
raise
+
def close(self):
-
+
if self.initialized:
try:
self.pool.close()
@@ -149,9 +155,10 @@
self.pool = None
self.initialized = False
+
@inlineCallbacks
def clean(self):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
@@ -165,14 +172,15 @@
else:
break
+
@inlineCallbacks
def execute(self, sql, *query_params):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
yield self.open()
-
+
try:
yield self._db_execute(sql, *query_params)
except Exception, e:
@@ -181,14 +189,15 @@
else:
break
+
@inlineCallbacks
def executescript(self, script):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
yield self.open()
-
+
try:
yield self._db_execute_script(script)
except Exception, e:
@@ -197,14 +206,15 @@
else:
break
+
@inlineCallbacks
def query(self, sql, *query_params):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
yield self.open()
-
+
try:
result = (yield self._db_all_values_for_sql(sql, *query_params))
except Exception, e:
@@ -215,14 +225,15 @@
returnValue(result)
+
@inlineCallbacks
def queryList(self, sql, *query_params):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
yield self.open()
-
+
try:
result = (yield self._db_values_for_sql(sql, *query_params))
except Exception, e:
@@ -233,14 +244,15 @@
returnValue(result)
+
@inlineCallbacks
def queryOne(self, sql, *query_params):
-
+
# Re-try at least once
for _ignore in (0, 1):
if not self.initialized:
yield self.open()
-
+
try:
result = (yield self._db_value_for_sql(sql, *query_params))
except Exception, e:
@@ -251,21 +263,25 @@
returnValue(result)
+
def _db_version(self):
"""
@return: the schema version assigned to this DB.
"""
raise NotImplementedError
-
+
+
def _db_type(self):
"""
@return: the collection type assigned to this DB.
"""
raise NotImplementedError
-
+
+
def _test_schema_table(self):
return self._test_table("CALDAV")
+
@inlineCallbacks
def _db_init(self):
"""
@@ -275,12 +291,12 @@
# TODO we need an exclusive lock of some kind here to prevent a race condition
# in which multiple processes try to create the tables.
-
yield self._db_init_schema_table()
yield self._db_init_data_tables()
yield self._db_recreate()
+
@inlineCallbacks
def _db_init_schema_table(self):
"""
@@ -310,12 +326,14 @@
""", (self._db_type(),)
)
+
def _db_init_data_tables(self):
"""
Initialise the underlying database tables.
"""
raise NotImplementedError
+
def _db_empty_data_tables(self):
"""
Delete the database tables.
@@ -323,7 +341,8 @@
# Implementations can override this to re-create data
pass
-
+
+
def _db_recreate(self):
"""
Recreate the database tables.
@@ -332,12 +351,13 @@
# Implementations can override this to re-create data
pass
+
@inlineCallbacks
def _db_upgrade(self, old_version):
"""
Upgrade the database tables.
"""
-
+
if self.persistent:
yield self._db_upgrade_data_tables(old_version)
yield self._db_upgrade_schema()
@@ -346,7 +366,8 @@
# DB upgrades they SHOULD override this method and handle those for better performance.
yield self._db_remove()
yield self._db_init()
-
+
+
def _db_upgrade_data_tables(self, old_version):
"""
Upgrade the data from an older version of the DB.
@@ -372,12 +393,14 @@
yield self._db_remove_data_tables()
yield self._db_remove_schema()
+
def _db_remove_data_tables(self):
"""
Remove all the data from an older version of the DB.
"""
raise NotImplementedError("Each database must remove its own tables.")
+
@inlineCallbacks
def _db_remove_schema(self):
"""
@@ -385,6 +408,7 @@
"""
yield self._db_execute("drop table if exists CALDAV")
+
@inlineCallbacks
def _db_all_values_for_sql(self, sql, *query_params):
"""
@@ -395,11 +419,12 @@
resulting from executing C{sql} with C{query_params}.
@raise AssertionError: if the query yields multiple columns.
"""
-
+
sql = self._prepare_statement(sql)
results = (yield self.pool.runQuery(sql, *query_params))
returnValue(tuple(results))
+
@inlineCallbacks
def _db_values_for_sql(self, sql, *query_params):
"""
@@ -411,11 +436,12 @@
resulting from executing C{sql} with C{query_params}.
@raise AssertionError: if the query yields multiple columns.
"""
-
+
sql = self._prepare_statement(sql)
results = (yield self.pool.runQuery(sql, *query_params))
returnValue(tuple([row[0] for row in results]))
+
@inlineCallbacks
def _db_value_for_sql(self, sql, *query_params):
"""
@@ -433,6 +459,7 @@
value = row
returnValue(value)
+
def _db_execute(self, sql, *query_params):
"""
Execute an SQL operation that returns None.
@@ -442,7 +469,7 @@
@return: an iterable of tuples for each row resulting from executing
C{sql} with C{query_params}.
"""
-
+
sql = self._prepare_statement(sql)
return self.pool.runOperation(sql, *query_params)
@@ -450,37 +477,43 @@
Since different databases support different types of columns and modifiers on those we need to
have an "abstract" way of specifying columns in our code and then map the abstract specifiers to
the underlying DB's allowed types.
-
+
Types we can use are:
-
+
integer
text
text(n)
date
serial
-
+
The " unique" modifier can be appended to any of those.
"""
def _map_column_types(self, type):
raise NotImplementedError
-
+
+
def _create_table(self, name, columns, ifnotexists=False):
raise NotImplementedError
+
def _test_table(self, name):
raise NotImplementedError
+
def _create_index(self, name, ontable, columns, ifnotexists=False):
raise NotImplementedError
+
def _prepare_statement(self, sql):
raise NotImplementedError
-
+
+
+
class ADBAPISqliteMixin(object):
@classmethod
def _map_column_types(self, coltype):
-
+
result = ""
splits = coltype.split()
if splits[0] == "integer":
@@ -493,15 +526,16 @@
result = "date"
elif splits[0] == "serial":
result = "integer primary key autoincrement"
-
+
if len(splits) > 1 and splits[1] == "unique":
result += " unique"
-
+
return result
+
@inlineCallbacks
def _create_table(self, name, columns, ifnotexists=False):
-
+
colDefs = ["%s %s" % (colname, self._map_column_types(coltype)) for colname, coltype in columns]
statement = "create table %s%s (%s)" % (
"if not exists " if ifnotexists else "",
@@ -510,6 +544,7 @@
)
yield self._db_execute(statement)
+
@inlineCallbacks
def _test_table(self, name):
result = (yield self._db_value_for_sql("""
@@ -518,9 +553,10 @@
""" % (name,)))
returnValue(result)
+
@inlineCallbacks
def _create_index(self, name, ontable, columns, ifnotexists=False):
-
+
statement = "create index %s%s on %s (%s)" % (
"if not exists " if ifnotexists else "",
name,
@@ -529,6 +565,7 @@
)
yield self._db_execute(statement)
+
def _prepare_statement(self, sql):
# We are going to use the sqlite syntax of :1, :2 etc for our
# internal statements so we do not need to remap those
@@ -537,10 +574,10 @@
if pgdb:
class ADBAPIPostgreSQLMixin(object):
-
+
@classmethod
def _map_column_types(self, coltype):
-
+
result = ""
splits = coltype.split()
if splits[0] == "integer":
@@ -553,32 +590,34 @@
result = "date"
elif splits[0] == "serial":
result = "serial"
-
+
if len(splits) > 1 and splits[1] == "unique":
result += " unique"
-
+
return result
-
+
+
@inlineCallbacks
def _create_table(self, name, columns, ifnotexists=False):
-
+
colDefs = ["%s %s" % (colname, self._map_column_types(coltype)) for colname, coltype in columns]
statement = "create table %s (%s)" % (
name,
", ".join(colDefs),
)
-
+
try:
yield self._db_execute(statement)
except pgdb.DatabaseError:
-
+
if not ifnotexists:
raise
-
+
result = (yield self._test_table(name))
if not result:
- raise
-
+ raise
+
+
@inlineCallbacks
def _test_table(self, name):
result = (yield self._db_value_for_sql("""
@@ -586,27 +625,29 @@
where tablename = '%s'
""" % (name.lower(),)))
returnValue(result)
-
+
+
@inlineCallbacks
def _create_index(self, name, ontable, columns, ifnotexists=False):
-
+
statement = "create index %s on %s (%s)" % (
name,
ontable,
", ".join(columns),
)
-
+
try:
yield self._db_execute(statement)
except pgdb.DatabaseError:
-
+
if not ifnotexists:
raise
-
+
result = (yield self._test_table(name))
if not result:
- raise
-
+ raise
+
+
@inlineCallbacks
def _db_init_schema_table(self):
"""
@@ -614,7 +655,7 @@
@param db_filename: the file name of the index database.
@param q: a database cursor to use.
"""
-
+
#
# CALDAV table keeps track of our schema version and type
#
@@ -623,7 +664,7 @@
("KEY", "text unique"),
("VALUE", "text unique"),
), True)
-
+
yield self._db_execute(
"""
insert into CALDAV (KEY, VALUE)
@@ -638,7 +679,8 @@
)
except pgdb.DatabaseError:
pass
-
+
+
def _prepare_statement(self, sql):
# Convert :1, :2 etc format into %s
ctr = 1
@@ -649,6 +691,6 @@
else:
class ADBAPIPostgreSQLMixin(object):
-
+
def __init__(self):
raise ConfigurationError("PostgreSQL module not available.")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,7 +19,7 @@
from twistedcaldav.datafilters.filter import CalendarFilter
from twistedcaldav.dateops import clipPeriod
from twistedcaldav.ical import Component
-from pycalendar.period import PyCalendarPeriod
+from pycalendar.period import Period
__all__ = [
"CalendarDataFilter",
@@ -161,7 +161,7 @@
for property in component.properties("FREEBUSY"):
newvalue = []
for period in property.value():
- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
+ clipped = clipPeriod(period.getValue(), Period(self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
if clipped:
newvalue.append(clipped)
if len(newvalue):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -28,9 +28,9 @@
"clipPeriod"
]
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.period import PyCalendarPeriod
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
+from pycalendar.period import Period
import datetime
import dateutil.tz
@@ -39,14 +39,14 @@
def normalizeForIndex(dt):
"""
- Normalize a L{PyCalendarDateTime} object for use in the Index.
+ Normalize a L{DateTime} object for use in the Index.
Convert to date-time in UTC.
- @param dt: a L{PyCalendarDateTime} object to normalize
- @return: the normalized PyCalendarDateTime
+ @param dt: a L{DateTime} object to normalize
+ @return: the normalized DateTime
"""
- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
dt = dt.duplicate()
if dt.isDateOnly():
dt.setDateOnly(False)
@@ -59,13 +59,15 @@
dt.adjustToUTC()
return dt
+
+
def normalizeToUTC(dt):
"""
- Normalize a L{PyCalendarDateTime} object to UTC.
+ Normalize a L{DateTime} object to UTC.
"""
- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
dt = dt.duplicate()
if dt.isDateOnly():
dt.setDateOnly(False)
@@ -79,16 +81,18 @@
dt.adjustToUTC()
return dt
+
+
def normalizeForExpand(dt):
"""
- Normalize a L{PyCalendarDateTime} object for use with the CalDAV expand option.
+ Normalize a L{DateTime} object for use with the CalDAV expand option.
Convert to date-time in UTC, leave date only and floating alone.
- @param dt: a L{PyCalendarDateTime} object to normalize
- @return: the normalized PyCalendarDateTime
+ @param dt: a L{DateTime} object to normalize
+ @return: the normalized DateTime
"""
- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
dt = dt.duplicate()
if dt.isDateOnly() or dt.floating():
return dt
@@ -96,41 +100,49 @@
dt.adjustToUTC()
return dt
+
+
def floatoffset(dt, pytz):
"""
Apply the timezone offset to the supplied time, then force tz to utc. This gives the local
date-time as if the local tz were UTC. It can be used in floating time comparisons with UTC date-times.
-
- @param dt: a L{PyCalendarDateTime} object to normalize
- @param pytz: a L{PyCalendarTimezone} object to apply offset from
- @return: the normalized PyCalendarDateTime
+
+ @param dt: a L{DateTime} object to normalize
+ @param pytz: a L{Timezone} object to apply offset from
+ @return: the normalized DateTime
"""
-
+
if pytz is None:
- pytz = PyCalendarTimezone(utc=True)
-
+ pytz = Timezone(utc=True)
+
dt = dt.duplicate()
dt.adjustTimezone(pytz)
dt.setTimezoneUTC(True)
return dt
+
+
def adjustFloatingToTimezone(dtadjust, dtcopyfrom, pytz=None):
-
+
dtadjust = dtadjust.duplicate()
dtadjust.setTimezone(pytz if pytz else dtcopyfrom.getTimezone())
return dtadjust
+
+
def compareDateTime(dt1, dt2, defaulttz=None):
-
+
if dt1.floating() and not dt2.floating():
dt1 = adjustFloatingToTimezone(dt1, dt2, defaulttz)
elif dt2.floating() and not dt1.floating():
dt2 = adjustFloatingToTimezone(dt2, dt1, defaulttz)
-
+
return dt1.compareDateTime(dt2)
-def differenceDateTime(start, end, defaulttz = None):
+
+def differenceDateTime(start, end, defaulttz=None):
+
if start.floating() and not end.floating():
start = adjustFloatingToTimezone(start, end, defaulttz)
elif end.floating() and not start.floating():
@@ -138,13 +150,19 @@
return end - start
-def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
+
+
+def timeRangesOverlap(start1, end1, start2, end2, defaulttz=None):
# Can't compare date-time and date only, so normalize
# to date only if they are mixed.
- if (start1 is not None) and not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly(): start1.setDateOnly(True)
- if (start2 is not None) and not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly(): start2.setDateOnly(True)
- if (end1 is not None) and not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly(): end1.setDateOnly(True)
- if (end2 is not None) and not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly(): end2.setDateOnly(True)
+ if (start1 is not None) and not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly():
+ start1.setDateOnly(True)
+ if (start2 is not None) and not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly():
+ start2.setDateOnly(True)
+ if (end1 is not None) and not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly():
+ end1.setDateOnly(True)
+ if (end2 is not None) and not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly():
+ end2.setDateOnly(True)
# Note that start times are inclusive and end times are not.
if start1 is not None and start2 is not None:
@@ -163,40 +181,41 @@
else:
return False
+
+
def normalizePeriodList(periods):
"""
Normalize the list of periods by merging overlapping or consecutive ranges
and sorting the list by each periods start.
- @param list: a list of tuples of L{PyCalendarPeriod}. The list is changed in place.
+ @param list: a list of tuples of L{Period}. The list is changed in place.
"""
-
+
# First sort the list
def sortPeriods(p1, p2):
"""
Compare two periods. Sort by their start and then end times.
- A period is a L{PyCalendarPeriod}.
+ A period is a L{Period}.
@param p1: first period
@param p2: second period
@return: 1 if p1>p2, 0 if p1==p2, -1 if p1<p2
"""
- assert isinstance(p1, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p1,)
- assert isinstance(p2, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p2,)
-
-
+ assert isinstance(p1, Period), "Period is not a Period: %r" % (p1,)
+ assert isinstance(p2, Period), "Period is not a Period: %r" % (p2,)
+
if p1.getStart() == p2.getStart():
cmp1 = p1.getEnd()
cmp2 = p2.getEnd()
else:
cmp1 = p1.getStart()
cmp2 = p2.getStart()
-
+
return compareDateTime(cmp1, cmp2)
for period in periods:
period.adjustToUTC()
periods.sort(cmp=sortPeriods)
-
+
# Now merge overlaps and consecutive periods
index = None
p = None
@@ -210,15 +229,17 @@
ie = periods[i].getEnd()
if (pe >= periods[i].getStart()):
if ie > pe:
- periods[index] = PyCalendarPeriod(periods[index].getStart(), ie)
+ periods[index] = Period(periods[index].getStart(), ie)
pe = ie
periods[i] = None
else:
index = i
p = periods[i]
- pe =p.getEnd()
+ pe = p.getEnd()
periods[:] = [x for x in periods if x]
+
+
def clipPeriod(period, clipPeriod):
"""
Clip the start/end period so that it lies entirely within the clip period.
@@ -234,20 +255,22 @@
if start < clipStart:
start = clipStart
-
+
if end > clipEnd:
end = clipEnd
-
+
if start >= end:
return None
else:
# Try to preserve use of duration in period
- result = PyCalendarPeriod(start, end)
+ result = Period(start, end)
result.setUseDuration(period.getUseDuration())
return result
+
+
def pyCalendarTodatetime(pydt):
-
+
if pydt.isDateOnly():
return datetime.date(year=pydt.getYear(), month=pydt.getMonth(), day=pydt.getDay())
else:
@@ -261,17 +284,19 @@
tzinfo=dateutil.tz.tzutc()
)
+
+
def parseSQLTimestampToPyCalendar(ts):
"""
- Parse an SQL formated timestamp into a PyCalendarDateTime
+ Parse an SQL formated timestamp into a DateTime
@param ts: the SQL timestamp
@type ts: C{str}
-
- @return: L{PyCalendarDateTime} result
+
+ @return: L{DateTime} result
"""
-
+
# Format is "%Y-%m-%d %H:%M:%S"
- return PyCalendarDateTime(
+ return DateTime(
year=int(ts[0:4]),
month=int(ts[5:7]),
day=int(ts[8:10]),
@@ -280,22 +305,26 @@
seconds=int(ts[17:19])
)
+
+
def parseSQLDateToPyCalendar(ts):
"""
- Parse an SQL formated date into a PyCalendarDateTime
+ Parse an SQL formated date into a DateTime
@param ts: the SQL date
@type ts: C{str}
-
- @return: L{PyCalendarDateTime} result
+
+ @return: L{DateTime} result
"""
-
+
# Format is "%Y-%m-%d", though Oracle may add zero time which we ignore
- return PyCalendarDateTime(
+ return DateTime(
year=int(ts[0:4]),
month=int(ts[5:7]),
day=int(ts[8:10])
)
+
+
def datetimeMktime(dt):
assert isinstance(dt, datetime.date)
@@ -303,4 +332,3 @@
if dt.tzinfo is None:
dt.replace(tzinfo=dateutil.tz.tzutc())
return calendar.timegm(dt.utctimetuple())
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -56,6 +56,8 @@
+ config.CalDAVComplianceClasses
)
+
+
class DirectoryAddressBookProvisioningResource (
ReadOnlyResourceMixIn,
CalDAVComplianceMixIn,
@@ -65,18 +67,21 @@
def defaultAccessControlList(self):
return config.ProvisioningResourceACL
+
def etag(self):
return succeed(ETag(str(uuid4())))
+
def contentType(self):
return MimeType("httpd", "unix-directory")
+
class DirectoryAddressBookHomeProvisioningResource (
DirectoryAddressBookProvisioningResource
):
"""
- Resource which provisions address book home collections as needed.
+ Resource which provisions address book home collections as needed.
"""
def __init__(self, directory, url, store):
"""
@@ -103,22 +108,27 @@
self.putChild(uidsResourceName, DirectoryAddressBookHomeUIDProvisioningResource(self))
+
def url(self):
return self._url
+
def listChildren(self):
return self.directory.recordTypes()
+
def principalCollections(self):
# FIXME: directory.principalCollection smells like a hack
# See DirectoryPrincipalProvisioningResource.__init__()
return self.directory.principalCollection.principalCollections()
+
def principalForRecord(self, record):
# FIXME: directory.principalCollection smells like a hack
# See DirectoryPrincipalProvisioningResource.__init__()
return self.directory.principalCollection.principalForRecord(record)
+
def homeForDirectoryRecord(self, record, request):
uidResource = self.getChild(uidsResourceName)
if uidResource is None:
@@ -126,17 +136,20 @@
else:
return uidResource.homeResourceForRecord(record, request)
+
##
# DAV
##
-
+
def isCollection(self):
return True
+
def displayName(self):
return "addressbooks"
+
class DirectoryAddressBookHomeTypeProvisioningResource (
CommonHomeTypeProvisioningResource,
DirectoryAddressBookProvisioningResource
@@ -159,6 +172,7 @@
self.recordType = recordType
self._parent = parent
+
def url(self):
return joinURL(self._parent.url(), self.recordType)
@@ -177,16 +191,19 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
+
def makeChild(self, name):
return None
+
##
# DAV
##
-
+
def isCollection(self):
return True
+
def displayName(self):
return self.recordType
@@ -194,13 +211,16 @@
# ACL
##
+
def principalCollections(self):
return self._parent.principalCollections()
+
def principalForRecord(self, record):
return self._parent.principalForRecord(record)
+
class DirectoryAddressBookHomeUIDProvisioningResource (
CommonUIDProvisioningResource,
DirectoryAddressBookProvisioningResource
@@ -210,11 +230,13 @@
enabledAttribute = 'enabledForAddressBooks'
+
def homeResourceCreator(self, record, transaction):
return DirectoryAddressBookHomeResource.createHomeResource(
self, record, transaction)
+
class DirectoryAddressBookHomeResource (AddressBookHomeResource):
"""
Address book home collection resource.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -25,11 +25,11 @@
from twext.python.log import Logger
from twistedcaldav.config import fullServerPath, config
-from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin,\
+from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin, \
ADBAPIPostgreSQLMixin
from twistedcaldav.directory import xmlaugmentsparser
from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
-from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement,\
+from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement, \
writeXML, readXML
from twistedcaldav.directory.util import normalizeUUID
@@ -56,7 +56,6 @@
uid,
enabled=False,
serverID="",
- partitionID="",
enabledForCalendaring=False,
autoSchedule=False,
autoScheduleMode="default",
@@ -67,7 +66,6 @@
self.uid = uid
self.enabled = enabled
self.serverID = serverID
- self.partitionID = partitionID
self.enabledForCalendaring = enabledForCalendaring
self.enabledForAddressBooks = enabledForAddressBooks
self.enabledForLogin = enabledForLogin
@@ -87,9 +85,9 @@
"""
Abstract base class for an augment record database.
"""
-
+
def __init__(self):
-
+
self.cachedRecords = {}
@@ -126,10 +124,10 @@
@param uid: directory UID to lookup
@type uid: C{str}
-
+
@return: L{Deferred}
"""
-
+
recordType = recordTypesMap[recordType]
result = (yield self._lookupAugmentRecord(uid))
@@ -166,6 +164,7 @@
result.clonedFromDefault = True
returnValue(result)
+
@inlineCallbacks
def getAllUIDs(self):
"""
@@ -173,21 +172,23 @@
@return: L{Deferred}
"""
-
+
raise NotImplementedError("Child class must define this.")
+
def _lookupAugmentRecord(self, uid):
"""
Get an AugmentRecord for the specified UID.
@param uid: directory UID to lookup
@type uid: C{str}
-
+
@return: L{Deferred}
"""
-
+
raise NotImplementedError("Child class must define this.")
+
@inlineCallbacks
def _cachedAugmentRecord(self, uid):
"""
@@ -195,59 +196,64 @@
@param uid: directory UID to lookup
@type uid: C{str}
-
+
@return: L{Deferred}
"""
-
+
if not uid in self.cachedRecords:
result = (yield self._lookupAugmentRecord(uid))
self.cachedRecords[uid] = result
returnValue(self.cachedRecords[uid])
+
def addAugmentRecords(self, records):
"""
Add an AugmentRecord to the DB.
@param record: augment records to add
@type record: C{list} of L{AugmentRecord}
-
+
@return: L{Deferred}
"""
raise NotImplementedError("Child class must define this.")
+
def removeAugmentRecords(self, uids):
"""
Remove AugmentRecords with the specified UIDs.
@param uid: directory UIDs to remove
@type uid: C{list} of C{str}
-
+
@return: L{Deferred}
"""
raise NotImplementedError("Child class must define this.")
+
def refresh(self):
"""
Refresh any cached data.
-
+
@return: L{Deferred}
"""
self.cachedRecords.clear()
return succeed(None)
-
+
+
def clean(self):
"""
Remove all records.
-
+
@return: L{Deferred}
"""
raise NotImplementedError("Child class must define this.")
+
class AugmentXMLDB(AugmentDB):
"""
XMLFile based augment database implementation.
@@ -257,7 +263,7 @@
super(AugmentXMLDB, self).__init__()
self.xmlFiles = [fullServerPath(config.DataRoot, path) for path in xmlFiles]
- self.xmlFileStats = { }
+ self.xmlFileStats = {}
for path in self.xmlFiles:
self.xmlFileStats[path] = (0, 0) # mtime, size
@@ -290,16 +296,17 @@
@param uid: directory UID to lookup
@type uid: C{str}
-
+
@return: L{Deferred}
"""
-
+
# May need to re-cache
if time.time() - self.lastCached > self.statSeconds:
self.refresh()
-
+
return succeed(self.db.get(uid))
+
def addAugmentRecords(self, records):
"""
Add an AugmentRecord to the DB.
@@ -308,13 +315,13 @@
@type records: C{list} of L{AugmentRecord}
@param update: C{True} if changing an existing record
@type update: C{bool}
-
+
@return: L{Deferred}
"""
# Look at each record and determine whether it is new or a modify
new_records = list()
- existing_records = list()
+ existing_records = list()
for record in records:
(existing_records if record.uid in self.db else new_records).append(record)
@@ -332,6 +339,7 @@
return succeed(None)
+
def _doAddToFile(self, xmlfile, records):
if not os.path.exists(xmlfile):
@@ -343,7 +351,6 @@
for record in self.db.itervalues():
self._addRecordToXMLDB(record, augments_node)
-
writeXML(xmlfile, augments_node)
# Set permissions
@@ -362,33 +369,33 @@
if uid != -1 and gid != -1:
os.chown(xmlfile, uid, gid)
-
_ignore_etree, augments_node = readXML(xmlfile)
# Create new record
for record in records:
self._addRecordToXMLDB(record, augments_node)
-
+
# Modify xmlfile
writeXML(xmlfile, augments_node)
-
+
+
def _doModifyInFile(self, xmlfile, records):
-
+
if not os.path.exists(xmlfile):
return
_ignore_etree, augments_node = readXML(xmlfile)
-
+
# Map uid->record for fast lookup
recordMap = dict([(record.uid, record) for record in records])
# Make sure UID is present
changed = False
for record_node in augments_node:
-
+
if record_node.tag != xmlaugmentsparser.ELEMENT_RECORD:
continue
-
+
uid = record_node.find(xmlaugmentsparser.ELEMENT_UID).text
if uid in recordMap:
# Modify record
@@ -400,13 +407,14 @@
if changed:
writeXML(xmlfile, augments_node)
+
def removeAugmentRecords(self, uids):
"""
Remove AugmentRecords with the specified UIDs.
@param uids: list of uids to remove
@type uids: C{list} of C{str}
-
+
@return: L{Deferred}
"""
@@ -423,10 +431,11 @@
return succeed(None)
+
def _doRemoveFromFile(self, xmlfile, uids):
-
+
_ignore_etree, augments_node = readXML(xmlfile)
-
+
# Remove all UIDs present
changed = False
for child in augments_node:
@@ -440,20 +449,19 @@
# Modify xmlfile
if changed:
writeXML(xmlfile, augments_node)
-
-
+
+
def _addRecordToXMLDB(self, record, parentNode):
record_node = addSubElement(parentNode, xmlaugmentsparser.ELEMENT_RECORD)
self._updateRecordInXMLDB(record, record_node)
+
def _updateRecordInXMLDB(self, record, recordNode):
del recordNode[:]
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_UID, record.uid)
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
if record.serverID:
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_SERVERID, record.serverID)
- if record.partitionID:
- addSubElement(recordNode, xmlaugmentsparser.ELEMENT_PARTITIONID, record.partitionID)
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLELOGIN, "true" if record.enabledForLogin else "false")
@@ -463,6 +471,7 @@
if record.autoAcceptGroup:
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOACCEPTGROUP, record.autoAcceptGroup)
+
def refresh(self):
"""
Refresh any cached data.
@@ -479,6 +488,7 @@
return succeed(None)
+
def clean(self):
"""
Remove all records.
@@ -487,6 +497,7 @@
self.removeAugmentRecords(self.db.keys())
return succeed(None)
+
def _shouldReparse(self, xmlFiles):
"""
Check to see whether any of the given files have been modified since
@@ -501,6 +512,7 @@
return True
return False
+
def _parseXML(self):
"""
Parse self.xmlFiles into AugmentRecords.
@@ -536,19 +548,22 @@
return results
+
+
class AugmentADAPI(AugmentDB, AbstractADBAPIDatabase):
"""
DBAPI based augment database implementation.
"""
- schema_version = "2"
- schema_type = "AugmentDB"
-
+ schema_version = "3"
+ schema_type = "AugmentDB"
+
def __init__(self, dbID, dbapiName, dbapiArgs, **kwargs):
-
+
AugmentDB.__init__(self)
AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
-
+
+
@inlineCallbacks
def getAllUIDs(self):
"""
@@ -556,11 +571,12 @@
@return: L{Deferred}
"""
-
+
# Query for the record information
results = (yield self.queryList("select UID from AUGMENTS", ()))
returnValue(results)
+
@inlineCallbacks
def _lookupAugmentRecord(self, uid):
"""
@@ -571,34 +587,34 @@
@return: L{Deferred}
"""
-
+
# Query for the record information
- results = (yield self.query("select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
+ results = (yield self.query("select UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
if not results:
returnValue(None)
else:
- uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
-
+ uid, enabled, serverid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
+
record = AugmentRecord(
- uid = uid,
- enabled = enabled == "T",
- serverID = serverid,
- partitionID = partitionid,
- enabledForCalendaring = enabledForCalendaring == "T",
- enabledForAddressBooks = enabledForAddressBooks == "T",
- enabledForLogin = enabledForLogin == "T",
- autoSchedule = autoSchedule == "T",
- autoScheduleMode = autoScheduleMode,
- autoAcceptGroup = autoAcceptGroup,
+ uid=uid,
+ enabled=enabled == "T",
+ serverID=serverid,
+ enabledForCalendaring=enabledForCalendaring == "T",
+ enabledForAddressBooks=enabledForAddressBooks == "T",
+ enabledForLogin=enabledForLogin == "T",
+ autoSchedule=autoSchedule == "T",
+ autoScheduleMode=autoScheduleMode,
+ autoAcceptGroup=autoAcceptGroup,
)
-
+
returnValue(record)
+
@inlineCallbacks
def addAugmentRecords(self, records):
for record in records:
-
+
results = (yield self.query("select UID from AUGMENTS where UID = :1", (record.uid,)))
update = len(results) > 0
@@ -607,6 +623,7 @@
else:
yield self._addRecord(record)
+
@inlineCallbacks
def removeAugmentRecords(self, uids):
"""
@@ -614,32 +631,36 @@
@param uids: list of uids to remove
@type uids: C{list} of C{str}
-
+
@return: L{Deferred}
"""
for uid in uids:
yield self.execute("delete from AUGMENTS where UID = :1", (uid,))
+
def clean(self):
"""
Remove all records.
"""
return self.execute("delete from AUGMENTS", ())
-
+
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
return AugmentADAPI.schema_version
-
+
+
def _db_type(self):
"""
@return: the collection type assigned to this index.
"""
return AugmentADAPI.schema_type
-
+
+
@inlineCallbacks
def _db_init_data_tables(self):
"""
@@ -652,45 +673,47 @@
yield self._create_table(
"AUGMENTS",
(
- ("UID", "text unique"),
- ("ENABLED", "text(1)"),
- ("SERVERID", "text"),
- ("PARTITIONID", "text"),
- ("CALENDARING", "text(1)"),
- ("ADDRESSBOOKS", "text(1)"),
- ("AUTOSCHEDULE", "text(1)"),
+ ("UID", "text unique"),
+ ("ENABLED", "text(1)"),
+ ("SERVERID", "text"),
+ ("CALENDARING", "text(1)"),
+ ("ADDRESSBOOKS", "text(1)"),
+ ("AUTOSCHEDULE", "text(1)"),
("AUTOSCHEDULEMODE", "text"),
- ("AUTOACCEPTGROUP", "text"),
- ("LOGINENABLED", "text(1)"),
+ ("AUTOACCEPTGROUP", "text"),
+ ("LOGINENABLED", "text(1)"),
),
ifnotexists=True,
)
+
@inlineCallbacks
def _db_empty_data_tables(self):
yield self._db_execute("delete from AUGMENTS")
+
+
class AugmentSqliteDB(ADBAPISqliteMixin, AugmentADAPI):
"""
Sqlite based augment database implementation.
"""
def __init__(self, dbpath):
-
+
ADBAPISqliteMixin.__init__(self)
AugmentADAPI.__init__(self, "Augments", "sqlite3", (fullServerPath(config.DataRoot, dbpath),))
+
@inlineCallbacks
def _addRecord(self, record):
yield self.execute(
"""insert or replace into AUGMENTS
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
(
record.uid,
"T" if record.enabled else "F",
record.serverID,
- record.partitionID,
"T" if record.enabledForCalendaring else "F",
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
@@ -700,30 +723,33 @@
)
)
+
def _modifyRecord(self, record):
return self._addRecord(record)
+
+
class AugmentPostgreSQLDB(ADBAPIPostgreSQLMixin, AugmentADAPI):
"""
PostgreSQL based augment database implementation.
"""
def __init__(self, host, database, user=None, password=None):
-
+
ADBAPIPostgreSQLMixin.__init__(self)
AugmentADAPI.__init__(self, "Augments", "pgdb", (), host=host, database=database, user=user, password=password,)
+
@inlineCallbacks
def _addRecord(self, record):
yield self.execute(
"""insert into AUGMENTS
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
(
record.uid,
"T" if record.enabled else "F",
record.serverID,
- record.partitionID,
"T" if record.enabledForCalendaring else "F",
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
@@ -733,17 +759,17 @@
)
)
+
@inlineCallbacks
def _modifyRecord(self, record):
yield self.execute(
"""update AUGMENTS set
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
- (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10) where UID = :11""",
+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
+ (:1, :2, :3, :4, :5, :6, :7, :8, :9) where UID = :10""",
(
record.uid,
"T" if record.enabled else "F",
record.serverID,
- record.partitionID,
"T" if record.enabledForCalendaring else "F",
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,7 +42,7 @@
Common ancestor for addressbook/calendar UID provisioning resources.
Must be mixed in to the hierarchy I{before} the appropriate resource type.
-
+
@ivar homeResourceTypeName: The name of the home resource type ('calendars'
or 'addressbooks').
@@ -78,13 +78,11 @@
assert len(name) > 4, "Directory record has an invalid GUID: %r" % (
name,)
-
- if record.locallyHosted():
+
+ if record.thisServer():
child = yield self.homeResourceCreator(record, transaction)
- elif record.thisServer():
+ else:
child = DirectoryReverseProxyResource(self, record)
- else:
- child = None # Use a redirect?
returnValue(child)
@@ -108,6 +106,7 @@
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
+
##
# ACL
##
@@ -115,12 +114,15 @@
def principalCollections(self):
return self.parent.principalCollections()
+
def principalForRecord(self, record):
return self.parent.principalForRecord(record)
+
+
##
# DAV
##
-
+
def isCollection(self):
return True
@@ -129,9 +131,11 @@
raise NotImplementedError(self.__class__.__name__ +
".getChild no longer exists.")
+
def displayName(self):
return uidsResourceName
+
def url(self):
return joinURL(self.parent.url(), uidsResourceName)
@@ -153,4 +157,3 @@
child = yield self._parent.homeForDirectoryRecord(record, request)
returnValue((child, segments[1:]))
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html 2013-12-14 06:28:16 UTC (rev 12110)
@@ -5,8 +5,7 @@
---------------------
Directory GUID: <t:slot name="directoryGUID"/>
Realm: <t:slot name="realm"/>
-<t:transparent t:render="serversEnabled">Hosted-At: <t:slot name="hostedAt"/>
-Partition: <t:slot name="partition"/></t:transparent>
+<t:transparent t:render="serversEnabled">Hosted-At: <t:slot name="hostedAt"/></t:transparent>
Principal Information
---------------------
GUID: <t:slot name="principalGUID"/>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -89,8 +89,8 @@
searchContext_location = "location"
searchContext_resource = "resource"
- searchContext_user = "user"
- searchContext_group = "group"
+ searchContext_user = "user"
+ searchContext_group = "group"
searchContext_attendee = "attendee"
aggregateService = None
@@ -628,6 +628,7 @@
self.expireSeconds = expireSeconds
self.lockSeconds = lockSeconds
+
def setGroupsFor(self, guid, memberships):
self.log.debug("set groups-for %s : %s" % (guid, memberships))
return self.set("groups-for:%s" %
@@ -675,7 +676,6 @@
return self.add("group-cacher-lock", "1", expireTime=self.lockSeconds)
-
def extendLock(self):
"""
Update the expiration time of the memcached lock
@@ -694,6 +694,7 @@
return self.delete("group-cacher-lock")
+
class GroupMembershipCacheUpdater(object):
"""
Responsible for updating memcached with group memberships. This will run
@@ -1130,7 +1131,7 @@
implements(IDirectoryRecord, ICalendarStoreDirectoryRecord)
def __repr__(self):
- return "<%s[%s@%s(%s)] %s(%s) %r @ %s/#%s>" % (
+ return "<%s[%s@%s(%s)] %s(%s) %r @ %s>" % (
self.__class__.__name__,
self.recordType,
self.service.guid,
@@ -1139,7 +1140,6 @@
",".join(self.shortNames),
self.fullName,
self.serverURI(),
- self.partitionID,
)
@@ -1175,7 +1175,6 @@
self.uid = uid
self.enabled = False
self.serverID = ""
- self.partitionID = ""
self.shortNames = shortNames
self.authIDs = authIDs
self.fullName = fullName
@@ -1257,7 +1256,6 @@
if augment:
self.enabled = augment.enabled
self.serverID = augment.serverID
- self.partitionID = augment.partitionID
self.enabledForCalendaring = augment.enabledForCalendaring
self.enabledForAddressBooks = augment.enabledForAddressBooks
self.autoSchedule = augment.autoSchedule
@@ -1278,7 +1276,6 @@
# Groups are by default always enabled
self.enabled = (self.recordType == self.service.recordType_groups)
self.serverID = ""
- self.partitionID = ""
self.enabledForCalendaring = False
self.enabledForAddressBooks = False
self.enabledForLogin = False
@@ -1496,46 +1493,9 @@
return None
- def partitionURI(self):
- """
- URL of the server hosting this record. Return None if hosted on this server.
- """
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.getPartitionURIForId(self.partitionID)
- return None
-
-
- def locallyHosted(self):
- """
- Hosted on this server/partition instance.
- """
-
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.thisServer and (not s.isPartitioned() or not self.partitionID or self.partitionID == config.ServerPartitionID)
- return True
-
-
- def effectivePartitionID(self):
- """
- Record partition ID taking into account whether the server is partitioned.
- """
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s and s.isPartitioned():
- return self.partitionID
- return ""
-
-
def thisServer(self):
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.thisServer
- return True
+ s = self.server()
+ return s.thisServer if s is not None else True
def autoAcceptMembers(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -32,20 +32,20 @@
realmName = Attribute("The name of the authentication realm this service represents.")
guid = Attribute("A GUID for this service.")
- def recordTypes():
+ def recordTypes(): #@NoSelf
"""
@return: a sequence of strings denoting the record types that
are kept in the directory. For example: C{["users",
"groups", "resources"]}.
"""
- def listRecords(recordType):
+ def listRecords(recordType): #@NoSelf
"""
@param type: the type of records to retrieve.
@return: an iterable of records of the given type.
"""
- def recordWithShortName(recordType, shortName):
+ def recordWithShortName(recordType, shortName): #@NoSelf
"""
@param recordType: the type of the record to look up.
@param shortName: the short name of the record to look up.
@@ -53,21 +53,21 @@
C{None} if no such record exists.
"""
- def recordWithUID(uid):
+ def recordWithUID(uid): #@NoSelf
"""
@param uid: the UID of the record to look up.
@return: an L{IDirectoryRecord} with the given UID, or C{None}
if no such record exists.
"""
- def recordWithGUID(guid):
+ def recordWithGUID(guid): #@NoSelf
"""
@param guid: the GUID of the record to look up.
@return: an L{IDirectoryRecord} with the given GUID, or
C{None} if no such record exists.
"""
- def recordWithCalendarUserAddress(address):
+ def recordWithCalendarUserAddress(address): #@NoSelf
"""
@param address: the calendar user address of the record to look up.
@type address: C{str}
@@ -81,7 +81,7 @@
directory service may not be aware of these addresses.
"""
- def recordWithCachedGroupsAlias(recordType, alias):
+ def recordWithCachedGroupsAlias(recordType, alias): #@NoSelf
"""
@param recordType: the type of the record to look up.
@param alias: the cached-groups alias of the record to look up.
@@ -91,14 +91,13 @@
alias, or C{None} if no such record is found.
"""
-
- def recordsMatchingFields(fields):
+ def recordsMatchingFields(fields): #@NoSelf
"""
@return: a deferred sequence of L{IDirectoryRecord}s which
match the given fields.
"""
- def recordsMatchingTokens(tokens, context=None):
+ def recordsMatchingTokens(tokens, context=None): #@NoSelf
"""
@param tokens: The tokens to search on
@type tokens: C{list} of C{str} (utf-8 bytes)
@@ -119,31 +118,31 @@
"attendee", only users, groups, and resources are considered.
"""
-
- def setRealm(realmName):
+ def setRealm(realmName): #@NoSelf
"""
Set a new realm name for this (and nested services if any)
@param realmName: the realm name this service should use.
"""
+
+
class IDirectoryRecord(Interface):
"""
Directory Record
"""
- service = Attribute("The L{IDirectoryService} this record exists in.")
- recordType = Attribute("The type of this record.")
- guid = Attribute("The GUID of this record.")
- uid = Attribute("The UID of this record.")
- enabled = Attribute("Determines whether this record should allow a principal to be created.")
- serverID = Attribute("Identifies the server that actually hosts data for the record.")
- partitionID = Attribute("Identifies the partition node that actually hosts data for the record.")
- shortNames = Attribute("The names for this record.")
- authIDs = Attribute("Alternative security identities for this record.")
- fullName = Attribute("The full name of this record.")
- firstName = Attribute("The first name of this record.")
- lastName = Attribute("The last name of this record.")
- emailAddresses = Attribute("The email addresses of this record.")
+ service = Attribute("The L{IDirectoryService} this record exists in.")
+ recordType = Attribute("The type of this record.")
+ guid = Attribute("The GUID of this record.")
+ uid = Attribute("The UID of this record.")
+ enabled = Attribute("Determines whether this record should allow a principal to be created.")
+ serverID = Attribute("Identifies the server that actually hosts data for the record.")
+ shortNames = Attribute("The names for this record.")
+ authIDs = Attribute("Alternative security identities for this record.")
+ fullName = Attribute("The full name of this record.")
+ firstName = Attribute("The first name of this record.")
+ lastName = Attribute("The last name of this record.")
+ emailAddresses = Attribute("The email addresses of this record.")
enabledForCalendaring = Attribute("Determines whether this record creates a principal with a calendar home.")
enabledForAddressBooks = Attribute("Determines whether this record creates a principal with an address book home.")
calendarUserAddresses = Attribute(
@@ -159,19 +158,19 @@
"""
)
- def members():
+ def members(): #@NoSelf
"""
@return: an iterable of L{IDirectoryRecord}s for the members of this
(group) record.
"""
- def groups():
+ def groups(): #@NoSelf
"""
@return: an iterable of L{IDirectoryRecord}s for the groups this
record is a member of.
"""
- def verifyCredentials(credentials):
+ def verifyCredentials(credentials): #@NoSelf
"""
Verify that the given credentials can authenticate the principal
represented by this record.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -75,12 +75,13 @@
return "<%s %r: %r>" % (self.__class__.__name__, self.realmName,
self.uri)
+
def __init__(self, params):
"""
@param params: a dictionary containing the following keys:
cacheTimeout, realmName, uri, tls, tlsCACertFile, tlsCACertDir,
tlsRequireCert, credentials, rdnSchema, groupSchema, resourceSchema
- partitionSchema
+ poddingSchema
"""
defaults = {
@@ -188,9 +189,8 @@
"readOnlyProxyAttr": None, # list of GUIDs
"autoAcceptGroupAttr": None, # single group GUID
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": None, # maps to augments server-id
- "partitionIdAttr": None, # maps to augments partition-id
},
}
ignored = None
@@ -222,7 +222,7 @@
self.rdnSchema = params["rdnSchema"]
self.groupSchema = params["groupSchema"]
self.resourceSchema = params["resourceSchema"]
- self.partitionSchema = params["partitionSchema"]
+ self.poddingSchema = params["poddingSchema"]
self.base = ldap.dn.str2dn(self.rdnSchema["base"])
@@ -272,10 +272,8 @@
attrSet.add(self.resourceSchema["proxyAttr"])
if self.resourceSchema["readOnlyProxyAttr"]:
attrSet.add(self.resourceSchema["readOnlyProxyAttr"])
- if self.partitionSchema["serverIdAttr"]:
- attrSet.add(self.partitionSchema["serverIdAttr"])
- if self.partitionSchema["partitionIdAttr"]:
- attrSet.add(self.partitionSchema["partitionIdAttr"])
+ if self.poddingSchema["serverIdAttr"]:
+ attrSet.add(self.poddingSchema["serverIdAttr"])
self.attrlist = list(attrSet)
self.typeDNs = {}
@@ -284,10 +282,8 @@
self.rdnSchema[recordType]["rdn"].lower()
) + self.base
-
self.ldap = None
-
# Separate LDAP connection used solely for authenticating clients
self.authLDAP = None
@@ -314,7 +310,7 @@
# Query the LDAP server
self.log.debug("Querying ldap for records matching base {base} and "
- "filter {filter} for attributes {attrs}.",
+ "filter {filter} for attributes {attrs}.",
base=ldap.dn.dn2str(base), filter=filterstr, attrs=self.attrlist)
# This takes a while, so if you don't want to have a "long request"
@@ -353,6 +349,7 @@
return records
+
@inlineCallbacks
def recordWithCachedGroupsAlias(self, recordType, alias):
"""
@@ -373,6 +370,7 @@
else:
returnValue(None)
+
def getExternalProxyAssignments(self):
"""
Retrieve proxy assignments for locations and resources from the
@@ -425,6 +423,7 @@
return assignments
+
def getLDAPConnection(self):
if self.ldap is None:
self.log.info("Connecting to LDAP {uri}", uri=repr(self.uri))
@@ -443,6 +442,7 @@
raise DirectoryConfigurationError()
return self.ldap
+
def createLDAPConnection(self):
"""
Create and configure LDAP connection
@@ -478,7 +478,7 @@
"""
TRIES = 3
- for i in xrange(TRIES):
+ for _ignore_i in xrange(TRIES):
self.log.debug("Authenticating %s" % (dn,))
if self.authLDAP is None:
@@ -555,11 +555,11 @@
self.log.warn("LDAP timeout exceeded: %d seconds" % (timeoutSeconds,))
except ldap.SERVER_DOWN:
self.ldap = None
- self.log.error("LDAP server unavailable (tried %d times)" % (i+1,))
+ self.log.error("LDAP server unavailable (tried %d times)" % (i + 1,))
continue
# change format, ignoring resultsType
- result = [resultItem for resultType, resultItem in s.allResults]
+ result = [resultItem for _ignore_resultType, resultItem in s.allResults]
totalTime = time.time() - startTime
if totalTime > self.warningThresholdSeconds:
@@ -769,7 +769,6 @@
"""
guid = None
- shortNames = ()
authIDs = set()
fullName = None
firstName = ""
@@ -891,30 +890,27 @@
autoAcceptGroup = self._getUniqueLdapAttribute(attrs,
self.resourceSchema["autoAcceptGroupAttr"])
- serverID = partitionID = None
- if self.partitionSchema["serverIdAttr"]:
+ serverID = None
+ if self.poddingSchema["serverIdAttr"]:
serverID = self._getUniqueLdapAttribute(attrs,
- self.partitionSchema["serverIdAttr"])
- if self.partitionSchema["partitionIdAttr"]:
- partitionID = self._getUniqueLdapAttribute(attrs,
- self.partitionSchema["partitionIdAttr"])
+ self.poddingSchema["serverIdAttr"])
record = LdapDirectoryRecord(
- service = self,
- recordType = recordType,
- guid = guid,
- shortNames = shortNames,
- authIDs = authIDs,
- fullName = fullName,
- firstName = firstName,
- lastName = lastName,
- emailAddresses = emailAddresses,
- uid = uid,
- dn = dn,
- memberGUIDs = memberGUIDs,
- extProxies = proxyGUIDs,
- extReadOnlyProxies = readOnlyProxyGUIDs,
- attrs = attrs,
+ service=self,
+ recordType=recordType,
+ guid=guid,
+ shortNames=shortNames,
+ authIDs=authIDs,
+ fullName=fullName,
+ firstName=firstName,
+ lastName=lastName,
+ emailAddresses=emailAddresses,
+ uid=uid,
+ dn=dn,
+ memberGUIDs=memberGUIDs,
+ extProxies=proxyGUIDs,
+ extReadOnlyProxies=readOnlyProxyGUIDs,
+ attrs=attrs,
)
if self.augmentService is not None:
@@ -924,7 +920,7 @@
# immediately.
d = self.augmentService.getAugmentRecord(record.guid,
recordType)
- d.addCallback(lambda x:record.addAugmentInformation(x))
+ d.addCallback(lambda x: record.addAugmentInformation(x))
else:
# Generate augment record based on information retrieved from LDAP
@@ -932,7 +928,6 @@
guid,
enabled=True,
serverID=serverID,
- partitionID=partitionID,
enabledForCalendaring=enabledForCalendaring,
autoSchedule=autoSchedule,
autoAcceptGroup=autoAcceptGroup,
@@ -965,7 +960,7 @@
matching the indexType and indexKey parameters.
recordTypes is a list of record types to limit the search to.
- indexType specifies one of the CachingDirectoryService contstants
+ indexType specifies one of the CachingDirectoryService constants
identifying which attribute to search on.
indexKey is the value to search for.
@@ -994,7 +989,8 @@
# Query on guid only works if guid attribute has been defined.
# Support for query on guid even if is auto-generated should
# be added.
- if not guidAttr: return
+ if not guidAttr:
+ return
filterstr = "(&%s(%s=%s))" % (filterstr, guidAttr, indexKey)
elif indexType == self.INDEX_TYPE_SHORTNAME:
@@ -1157,7 +1153,6 @@
self.log.debug("LDAP search returned %d results, %d usable" % (len(results), typeCounts[recordType]))
-
typeCountsStr = ", ".join(["%s:%d" % (rt, ct) for (rt, ct) in typeCounts.iteritems()])
totalTime = time.time() - startTime
self.log.info("Calendar user search for %s matched %d records (%s) in %.2f seconds" % (tokens, len(records), typeCountsStr, totalTime))
@@ -1172,7 +1167,7 @@
"""
records = []
- self.log.debug("Peforming principal property search for %s" % (fields,))
+ self.log.debug("Performing principal property search for %s" % (fields,))
if recordType is None:
# Make a copy since we're modifying it
@@ -1324,6 +1319,7 @@
returnValue(recordsByAlias.values())
+
def recordTypeForDN(self, dnStr):
"""
Examine a DN to determine which recordType it belongs to
@@ -1339,6 +1335,7 @@
return None
+
def dnContainedIn(child, parent):
"""
Return True if child dn is contained within parent dn, otherwise False.
@@ -1346,6 +1343,7 @@
return child[-len(parent):] == parent
+
def normalizeDNstr(dnStr):
"""
Convert to lowercase and remove extra whitespace
@@ -1356,6 +1354,7 @@
return ' '.join(ldap.dn.dn2str(ldap.dn.str2dn(dnStr.lower())).split())
+
def _convertValue(value, matchType):
if matchType == "starts-with":
value = "%s*" % (ldapEsc(value),)
@@ -1366,6 +1365,8 @@
value = ldapEsc(value)
return value
+
+
def buildFilter(recordType, mapping, fields, operand="or", optimizeMultiName=False):
"""
Create an LDAP filter string from a list of tuples representing directory
@@ -1403,8 +1404,8 @@
# try the various firstName/lastName permutations:
if recordType == "users":
converted = []
- for firstName, firstCaseless, firstMatchType in combined["firstName"]:
- for lastName, lastCaseless, lastMatchType in combined["lastName"]:
+ for firstName, _ignore_firstCaseless, firstMatchType in combined["firstName"]:
+ for lastName, _ignore_lastCaseless, lastMatchType in combined["lastName"]:
if firstName != lastName:
firstValue = _convertValue(firstName, firstMatchType)
lastValue = _convertValue(lastName, lastMatchType)
@@ -1427,7 +1428,7 @@
# name, guid)
additional = []
for key in ("recordName", "guid"):
- if mapping.has_key(key):
+ if key in mapping:
additional.append("(%s=*)" % (mapping.get(key),))
if additional:
filterstr = "(&%s%s)" % ("".join(additional), filterstr)
@@ -1435,6 +1436,7 @@
return filterstr
+
def buildFilterFromTokens(recordType, mapping, tokens, extra=None):
"""
Create an LDAP filter string from a list of query tokens. Each token is
@@ -1497,6 +1499,7 @@
return filterStr
+
class LdapDirectoryRecord(CachingDirectoryRecord):
"""
LDAP implementation of L{IDirectoryRecord}.
@@ -1509,18 +1512,18 @@
attrs
):
super(LdapDirectoryRecord, self).__init__(
- service = service,
- recordType = recordType,
- guid = guid,
- shortNames = shortNames,
- authIDs = authIDs,
- fullName = fullName,
- firstName = firstName,
- lastName = lastName,
- emailAddresses = emailAddresses,
- extProxies = extProxies,
- extReadOnlyProxies = extReadOnlyProxies,
- uid = uid,
+ service=service,
+ recordType=recordType,
+ guid=guid,
+ shortNames=shortNames,
+ authIDs=authIDs,
+ fullName=fullName,
+ firstName=firstName,
+ lastName=lastName,
+ emailAddresses=emailAddresses,
+ extProxies=extProxies,
+ extReadOnlyProxies=extReadOnlyProxies,
+ uid=uid,
)
# Save attributes of dn and attrs in case you might need them later
@@ -1548,6 +1551,7 @@
self._members_storage = self._members()
return self._members_storage
+
def _members(self):
""" Fault in records for the members of this group """
@@ -1578,7 +1582,7 @@
dn, attrs = result.pop()
dn = normalizeDNstr(dn)
- self.log.debug("Retrieved: %s %s" % (dn,attrs))
+ self.log.debug("Retrieved: %s %s" % (dn, attrs))
recordType = self.service.recordTypeForDN(dn)
if recordType is None:
self.log.error("Unable to map %s to a record type" % (dn,))
@@ -1595,6 +1599,7 @@
return results
+
def groups(self):
""" Return the records representing groups this record is a member of """
try:
@@ -1603,6 +1608,7 @@
self._groups_storage = self._groups()
return self._groups_storage
+
def _groups(self):
""" Fault in the groups of which this record is a member """
@@ -1618,7 +1624,7 @@
if len(membersAttrs) == 1:
filterstr = "(%s=%s)" % (membersAttrs[0], self._memberId)
else:
- filterstr = "(|%s)" % ( "".join(
+ filterstr = "(|%s)" % ("".join(
["(%s=%s)" % (a, self._memberId) for a in membersAttrs]
),
)
@@ -1641,6 +1647,7 @@
return groups
+
def cachedGroupsAlias(self):
"""
See directory.py for full description
@@ -1650,6 +1657,7 @@
"""
return self._memberId
+
def memberGUIDs(self):
return set(self._memberGUIDs)
@@ -1717,10 +1725,13 @@
return super(LdapDirectoryRecord, self).verifyCredentials(credentials)
+
class MissingRecordNameException(Exception):
""" Raised when LDAP record is missing recordName """
pass
+
+
class MissingGuidException(Exception):
""" Raised when LDAP record is missing guidAttr and it's required """
pass
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -35,9 +35,9 @@
from tempfile import mkstemp, gettempdir
from random import random
-from pycalendar.n import N
-from pycalendar.adr import Adr
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.vcard.n import N
+from pycalendar.vcard.adr import Adr
+from pycalendar.datetime import DateTime
from socket import getfqdn
@@ -1494,7 +1494,7 @@
birthdate = self.isoDateStringForDateAttribute(dsattributes.kDS1AttrBirthday)
if birthdate:
- vcard.addProperty(Property("BDAY", PyCalendarDateTime.parseText(birthdate, fullISO=True)))
+ vcard.addProperty(Property("BDAY", DateTime.parseText(birthdate, fullISO=True)))
# 3.2 Delivery Addressing Types http://tools.ietf.org/html/rfc2426#section-3.2
#
@@ -1685,7 +1685,7 @@
# 3.6.4 REV Type Definition
revDate = self.isoDateStringForDateAttribute(dsattributes.kDS1AttrModificationTimestamp)
if revDate:
- vcard.addProperty(Property("REV", PyCalendarDateTime.parseText(revDate, fullISO=True)))
+ vcard.addProperty(Property("REV", DateTime.parseText(revDate, fullISO=True)))
"""
# UNIMPLEMENTED:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -601,7 +601,6 @@
record = self.resource.record
return tag.fillSlots(
hostedAt=str(record.serverURI()),
- partition=str(record.effectivePartitionID()),
)
@@ -1066,14 +1065,6 @@
return self.record.server()
- def partitionURI(self):
- return self.record.partitionURI()
-
-
- def locallyHosted(self):
- return self.record.locallyHosted()
-
-
def thisServer(self):
return self.record.thisServer()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -25,14 +25,13 @@
__all__ = ["DirectoryReverseProxyResource"]
class DirectoryReverseProxyResource(ReverseProxyResource):
-
+
def __init__(self, parent, record):
self.parent = parent
self.record = record
-
- super(DirectoryReverseProxyResource, self).__init__(self.record.partitionID)
-
- def url(self):
- return joinURL(self.parent.url(), self.record.uid)
+ super(DirectoryReverseProxyResource, self).__init__(self.record.serverID)
+
+ def url(self):
+ return joinURL(self.parent.url(), self.record.uid)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,40 +24,34 @@
<enable>true</enable>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
- <partition-id>00001</partition-id>
</record>
<record>
<uid>Location-Default</uid>
<enable>true</enable>
<enable-calendar>true</enable-calendar>
- <partition-id>00004</partition-id>
<auto-schedule>true</auto-schedule>
</record>
<record>
<uid>Location-AA*</uid>
<enable>true</enable>
<enable-calendar>true</enable-calendar>
- <partition-id>00005</partition-id>
<auto-schedule>true</auto-schedule>
</record>
<record>
<uid>Resource-Default</uid>
<enable>true</enable>
<enable-calendar>true</enable-calendar>
- <partition-id>00006</partition-id>
<auto-schedule>true</auto-schedule>
</record>
<record>
<uid>Resource-AA*</uid>
<enable>true</enable>
<enable-calendar>true</enable-calendar>
- <partition-id>00007</partition-id>
<auto-schedule>true</auto-schedule>
</record>
<record>
<uid>AA*</uid>
<enable>true</enable>
- <partition-id>00001</partition-id>
</record>
<record>
<uid>AB*</uid>
@@ -66,14 +60,12 @@
<record>
<uid>B*</uid>
<enable>true</enable>
- <partition-id>00002</partition-id>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
</record>
<record>
<uid>C*</uid>
<enable>true</enable>
- <partition-id>00003</partition-id>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
<auto-schedule>true</auto-schedule>
@@ -102,17 +94,14 @@
<record>
<uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
<enable>true</enable>
- <partition-id>00001</partition-id>
</record>
<record>
<uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
<enable>true</enable>
- <partition-id>00002</partition-id>
</record>
<record>
<uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
<enable>true</enable>
- <partition-id>00002</partition-id>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
<auto-schedule>true</auto-schedule>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,12 +42,10 @@
<record>
<uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
<enable>true</enable>
- <partition-id>00001</partition-id>
</record>
<record>
<uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
<enable>true</enable>
- <partition-id>00002</partition-id>
</record>
<record>
<uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -175,7 +175,6 @@
Augments for the directory service records to add calendar specific attributes.
A variety of augment services are available for use.
- When using a partitioned server, a service that can be accessed from each host will be needed.
-->
<!-- XML File Augment Service -->
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>users</key>
- <array>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>alice</string>
- <key>username</key>
- <string>alice</string>
- </dict>
- </array>
- </dict>
-</plist>
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>users</key>
- <array>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>alice</string>
- <key>username</key>
- <string>alice</string>
- </dict>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>bob</string>
- <key>username</key>
- <string>bob</string>
- </dict>
- </array>
- </dict>
-</plist>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -36,7 +36,8 @@
recordTypes.add(prefix + recordType)
return recordTypes
- def _records(key):
+
+ def _records(key): #@NoSelf
def get(self):
records = {}
for prefix, testClass in testServices:
@@ -58,6 +59,7 @@
recordTypePrefixes = tuple(s[0] for s in testServices)
+
def service(self):
"""
Returns an IDirectoryService.
@@ -71,9 +73,9 @@
)
xmlService.recordTypePrefix = xml_prefix
-
return AggregateDirectoryService((xmlService,), None)
+
def test_setRealm(self):
"""
setRealm gets propagated to nested services
@@ -82,4 +84,3 @@
aggregatedService.setRealm("foo.example.com")
for service in aggregatedService._recordTypes.values():
self.assertEquals("foo.example.com", service.realmName)
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,7 +15,7 @@
##
from twistedcaldav.test.util import TestCase
-from twistedcaldav.directory.augment import AugmentXMLDB, AugmentSqliteDB,\
+from twistedcaldav.directory.augment import AugmentXMLDB, AugmentSqliteDB, \
AugmentPostgreSQLDB, AugmentRecord
from twistedcaldav.directory.directory import DirectoryService
from twisted.internet.defer import inlineCallbacks
@@ -32,46 +32,46 @@
xmlFileNormalization = os.path.join(os.path.dirname(__file__), "augments-normalization.xml")
testRecords = (
- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True, "partitionID":"00002", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"default"},
- {"uid":"C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"default"},
- {"uid":"8AB34DF9-0297-4BA3-AADB-DB557DDD21E7", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
- {"uid":"FC674703-8008-4A77-B80E-0DB55A9CE620", "enabledForLogin":False,}, # Explicitly false
- {"uid":"B473DC32-1B0D-45EE-9BAC-DA878AE9CE74", "enabledForLogin":True,}, # Explicitly True
- {"uid":"9F2B176D-B3F5-483A-AA63-0A1FC6E6D54B", "enabledForLogin":True,}, # Default is True
+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "default"},
+ {"uid": "C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "default"},
+ {"uid": "8AB34DF9-0297-4BA3-AADB-DB557DDD21E7", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
+ {"uid": "FC674703-8008-4A77-B80E-0DB55A9CE620", "enabledForLogin": False, }, # Explicitly false
+ {"uid": "B473DC32-1B0D-45EE-9BAC-DA878AE9CE74", "enabledForLogin": True, }, # Explicitly True
+ {"uid": "9F2B176D-B3F5-483A-AA63-0A1FC6E6D54B", "enabledForLogin": True, }, # Default is True
)
testRecordWildcardDefault = (
- {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00001", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled":True, "partitionID":"00003", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
- {"uid":"AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled":True, "partitionID":"00003", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
+ {"uid": "A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "AA5F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
+ {"uid": "AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
)
testRecordTypeDefault = (
- ("locations", {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00004", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("locations", {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00005", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("resources", {"uid":"A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00006", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("resources", {"uid":"AA6F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00007", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
+ ("locations", {"uid": "A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("locations", {"uid": "AA5F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("resources", {"uid": "A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("resources", {"uid": "AA6F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
)
testAddRecords = (
- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
)
testModifyRecords = (
- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
)
@@ -79,27 +79,31 @@
@inlineCallbacks
def _checkRecord(self, db, items, recordType=DirectoryService.recordType_users):
-
+
record = (yield db.getAugmentRecord(items["uid"], recordType))
self.assertTrue(record is not None, "Failed record uid: %s" % (items["uid"],))
-
- for k,v in items.iteritems():
- self.assertEqual(getattr(record, k), v, "Failed record uid: %s, attribute: %s" % (items["uid"], k, ))
+ for k, v in items.iteritems():
+ self.assertEqual(getattr(record, k), v, "Failed record uid: %s, attribute: %s" % (items["uid"], k,))
+
+
@inlineCallbacks
def _checkRecordExists(self, db, uid, recordType=DirectoryService.recordType_users):
-
+
record = (yield db.getAugmentRecord(uid, recordType))
self.assertTrue(record is not None, "Failed record uid: %s" % (uid,))
+
+
class AugmentTestsMixin(object):
def _db(self, dbpath=None):
raise NotImplementedError
+
@inlineCallbacks
def test_read(self):
-
+
dbpath = os.path.abspath(self.mktemp())
db = self._db(dbpath)
@@ -113,9 +117,10 @@
# in the DB
yield self._checkRecordExists(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
+
@inlineCallbacks
def test_read_default(self):
-
+
dbpath = os.path.abspath(self.mktemp())
db = self._db(dbpath)
@@ -132,6 +137,7 @@
for item in testRecordWildcardDefault:
yield self._checkRecord(db, item)
+
@inlineCallbacks
def test_read_typed_default(self):
"""
@@ -159,7 +165,7 @@
@inlineCallbacks
def test_add_modify(self):
-
+
dbpath = os.path.abspath(self.mktemp())
db = self._db(dbpath)
@@ -195,11 +201,13 @@
yield self._checkRecord(newdb, item)
yield self._checkRecord(newdb, testModifyRecords[0])
+
+
class AugmentXMLTests(AugmentTests):
@inlineCallbacks
def test_read(self):
-
+
db = AugmentXMLDB((xmlFile,))
for item in testRecords:
@@ -209,9 +217,10 @@
# in the DB
yield self._checkRecordExists(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
+
@inlineCallbacks
def test_read_default(self):
-
+
db = AugmentXMLDB((xmlFileDefault,))
for item in testRecords:
@@ -220,8 +229,9 @@
for item in testRecordWildcardDefault:
yield self._checkRecord(db, item)
+
def test_parseErrors(self):
-
+
db = {}
self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO(""), db)
self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
@@ -249,13 +259,14 @@
</record>
"""), db)
+
@inlineCallbacks
def test_add_modify(self):
-
+
# Duplicate file as we will change it
newxmlfile = FilePath(self.mktemp())
FilePath(xmlFile).copyTo(newxmlfile)
-
+
db = AugmentXMLDB((newxmlfile.path,))
for item in testRecords:
@@ -283,6 +294,7 @@
yield self._checkRecord(newdb, item)
yield self._checkRecord(newdb, testModifyRecords[0])
+
def test_shouldReparse(self):
"""
Verify that a change to the file will get noticed
@@ -294,6 +306,7 @@
newxmlfile.setContent("") # Change the file
self.assertTrue(db._shouldReparse([newxmlfile.path])) # Need to parse
+
def test_refresh(self):
"""
Ensure that a refresh without any file changes doesn't zero out the
@@ -304,6 +317,7 @@
dbxml.refresh()
self.assertEquals(keys, dbxml.db.keys())
+
def uidsFromFile(self, filename):
"""
Return all uids from the augments xml file
@@ -316,6 +330,7 @@
uid = record_node.find(xmlaugmentsparser.ELEMENT_UID).text
yield uid
+
def test_normalize(self):
"""
Ensure augment uids are normalized upon opening
@@ -328,11 +343,15 @@
uids = list(self.uidsFromFile(newxmlfile.path))
self.assertEquals(uids, ['AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA'])
+
+
class AugmentSqliteTests(AugmentTests, AugmentTestsMixin):
def _db(self, dbpath=None):
return AugmentSqliteDB(dbpath if dbpath else os.path.abspath(self.mktemp()))
+
+
class AugmentPostgreSQLTests(AugmentTests, AugmentTestsMixin):
def _db(self, dbpath=None):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -38,6 +38,7 @@
DirectoryService.recordType_resources,
)
+
def queryDirectory(self, recordTypes, indexType, indexKey):
self.queried = True
@@ -58,42 +59,47 @@
if cacheIt:
cacheRecord = CachingDirectoryRecord(
- service = self,
- recordType = recordType,
- guid = record.get("guid"),
- shortNames = record.get("shortname"),
- authIDs = record.get("authid"),
- fullName = record.get("fullName"),
- firstName = "",
- lastName = "",
- emailAddresses = record.get("email"),
- )
-
+ service=self,
+ recordType=recordType,
+ guid=record.get("guid"),
+ shortNames=record.get("shortname"),
+ authIDs=record.get("authid"),
+ fullName=record.get("fullName"),
+ firstName="",
+ lastName="",
+ emailAddresses=record.get("email"),
+ )
+
augmentRecord = AugmentRecord(
- uid = cacheRecord.guid,
+ uid=cacheRecord.guid,
enabled=True,
- enabledForCalendaring = True,
+ enabledForCalendaring=True,
)
-
+
cacheRecord.addAugmentInformation(augmentRecord)
self.recordCacheForType(recordType).addRecord(cacheRecord,
indexType, indexKey)
+
+
class CachingDirectoryTest(TestCase):
-
+
baseGUID = str(uuid4())
+
def setUp(self):
super(CachingDirectoryTest, self).setUp()
self.service = TestDirectoryService()
self.service.queried = False
+
def loadRecords(self, records):
self.service._initCaches()
self.service.fakerecords = records
self.service.queried = False
+
def fakeRecord(
self,
fullName,
@@ -109,15 +115,15 @@
shortNames = (self.shortNameForFullName(fullName),)
if multinames:
shortNames += (fullName,)
-
+
if guid is None:
guid = self.guidForShortName(shortNames[0], recordType=recordType)
else:
guid = guid.lower()
-
+
if emails is None:
emails = ("%s at example.com" % (shortNames[0],),)
-
+
attrs = {
"fullName": fullName,
"guid": guid,
@@ -126,35 +132,38 @@
"cua": tuple(["mailto:%s" % email for email in emails]),
"authid": tuple(["Kerberos:%s" % email for email in emails])
}
-
+
if members:
attrs["members"] = members
-
+
if resourceInfo:
attrs["resourceInfo"] = resourceInfo
-
+
return attrs
-
+
+
def shortNameForFullName(self, fullName):
return fullName.lower().replace(" ", "")
-
+
+
def guidForShortName(self, shortName, recordType=""):
return uuidFromName(self.baseGUID, "%s%s" % (recordType, shortName))
+
def dummyRecords(self):
SIZE = 10
records = {
DirectoryService.recordType_users: [
- self.fakeRecord("User %02d" % x, DirectoryService.recordType_users, multinames=(x>5)) for x in range(1,SIZE+1)
+ self.fakeRecord("User %02d" % x, DirectoryService.recordType_users, multinames=(x > 5)) for x in range(1, SIZE + 1)
],
DirectoryService.recordType_groups: [
- self.fakeRecord("Group %02d" % x, DirectoryService.recordType_groups) for x in range(1,SIZE+1)
+ self.fakeRecord("Group %02d" % x, DirectoryService.recordType_groups) for x in range(1, SIZE + 1)
],
DirectoryService.recordType_resources: [
- self.fakeRecord("Resource %02d" % x, DirectoryService.recordType_resources) for x in range(1,SIZE+1)
+ self.fakeRecord("Resource %02d" % x, DirectoryService.recordType_resources) for x in range(1, SIZE + 1)
],
DirectoryService.recordType_locations: [
- self.fakeRecord("Location %02d" % x, DirectoryService.recordType_locations) for x in range(1,SIZE+1)
+ self.fakeRecord("Location %02d" % x, DirectoryService.recordType_locations) for x in range(1, SIZE + 1)
],
}
# Add duplicate shortnames
@@ -165,14 +174,17 @@
self.loadRecords(records)
+
def verifyRecords(self, recordType, expectedGUIDs):
-
+
records = self.service.listRecords(recordType)
recordGUIDs = set([record.guid for record in records])
self.assertEqual(recordGUIDs, expectedGUIDs)
+
+
class GUIDLookups(CachingDirectoryTest):
-
+
def test_emptylist(self):
self.dummyRecords()
@@ -180,7 +192,8 @@
self.verifyRecords(DirectoryService.recordType_groups, set())
self.verifyRecords(DirectoryService.recordType_resources, set())
self.verifyRecords(DirectoryService.recordType_locations, set())
-
+
+
def test_cacheoneguid(self):
self.dummyRecords()
@@ -200,7 +213,8 @@
# Make sure guid is case-insensitive
self.assertTrue(self.service.recordWithGUID(self.guidForShortName("user01", recordType=DirectoryService.recordType_users).lower()) is not None)
-
+
+
def test_cacheoneshortname(self):
self.dummyRecords()
@@ -224,6 +238,7 @@
) is not None)
self.assertFalse(self.service.queried)
+
def test_cacheoneemail(self):
self.dummyRecords()
@@ -245,6 +260,7 @@
) is not None)
self.assertFalse(self.service.queried)
+
def test_cacheonePrincipalsURLWithUIDS(self):
self.dummyRecords()
@@ -267,6 +283,7 @@
) is not None)
self.assertFalse(self.service.queried)
+
def test_cacheonePrincipalsURLWithUsers(self):
self.dummyRecords()
@@ -288,6 +305,7 @@
) is not None)
self.assertFalse(self.service.queried)
+
def test_cacheoneauthid(self):
self.dummyRecords()
@@ -309,6 +327,7 @@
) is not None)
self.assertFalse(self.service.queried)
+
def test_negativeCaching(self):
self.dummyRecords()
@@ -324,7 +343,6 @@
self.assertEquals(self.service.recordWithGUID(self.guidForShortName("missing")), None)
self.assertTrue(self.service.queried)
-
# However, if negativeCaching is on, a miss is recorded as such,
# preventing a similar queryDirectory( ) until cacheTimeout passes
self.service.negativeCaching = True
@@ -345,6 +363,7 @@
self.assertEquals(self.service.recordWithGUID(self.guidForShortName("missing")), None)
self.assertTrue(self.service.queried)
+
def test_duplicateShortNames(self):
"""
Verify that when looking up records having duplicate short-names, the record of the
@@ -370,6 +389,7 @@
"Duplicate")
self.assertEquals(record.recordType, DirectoryService.recordType_locations)
+
def test_generateMemcacheKey(self):
"""
Verify keys are correctly generated based on the index type -- if index type is
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -55,7 +55,7 @@
challengeResponse = ('digest',
{'nonce': challengeNonce,
'qop': 'auth', 'realm': 'test realm',
- 'algorithm': 'md5',})
+ 'algorithm': 'md5', })
cnonce = "29fc54aa1641c6fa0e151419361c8f23"
@@ -116,6 +116,7 @@
self.namespace2
))
+
def getDigestResponse(self, challenge, ncount):
"""
Calculate the response for the given challenge
@@ -146,6 +147,7 @@
)
return expected
+
def getDigestResponseComma(self, challenge, ncount):
"""
Calculate the response for the given challenge
@@ -176,6 +178,7 @@
)
return expected
+
@inlineCallbacks
def assertRaisesDeferred(self, exception, f, *args, **kwargs):
try:
@@ -191,6 +194,7 @@
raise self.failureException('%s not raised (%r returned)'
% (exception.__name__, result))
+
@inlineCallbacks
def test_getChallenge(self):
"""
@@ -203,14 +207,15 @@
self.assertEquals(challenge['qop'], 'auth')
self.assertEquals(challenge['realm'], 'test realm')
self.assertEquals(challenge['algorithm'], 'md5')
- self.assertTrue(challenge.has_key("nonce"))
+ self.assertTrue("nonce" in challenge)
challenge = (yield self.credentialFactories[1].getChallenge(clientAddress))
- self.assertFalse(challenge.has_key('qop'))
+ self.assertFalse('qop' in challenge)
self.assertEquals(challenge['realm'], 'test realm')
self.assertEquals(challenge['algorithm'], 'md5')
- self.assertTrue(challenge.has_key("nonce"))
+ self.assertTrue("nonce" in challenge)
+
@inlineCallbacks
def test_response(self):
"""
@@ -219,15 +224,16 @@
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
self.failUnless(creds.checkPassword('password'))
+
@inlineCallbacks
def test_multiResponse(self):
"""
@@ -237,23 +243,24 @@
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
self.failUnless(creds.checkPassword('password'))
-
+
clientResponse = authRequest2[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000002"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
self.failUnless(creds.checkPassword('password'))
+
@inlineCallbacks
def test_failsWithDifferentMethod(self):
"""
@@ -263,16 +270,17 @@
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse,
SimpleRequest(None, 'POST', '/')))
self.failIf(creds.checkPassword('password'))
+
@inlineCallbacks
def test_noUsername(self):
"""
@@ -287,7 +295,7 @@
namelessAuthRequest,
_trivial_GET()))
self.assertEquals(str(e), "Invalid response, no username given.")
-
+
# Check for an empty username
e = (yield self.assertRaisesDeferred(error.LoginFailed,
factory.decode,
@@ -295,6 +303,7 @@
_trivial_GET()))
self.assertEquals(str(e), "Invalid response, no username given.")
+
@inlineCallbacks
def test_noNonce(self):
"""
@@ -308,6 +317,7 @@
_trivial_GET()))
self.assertEquals(str(e), "Invalid response, no nonce given.")
+
@inlineCallbacks
def test_emptyAttribute(self):
"""
@@ -323,6 +333,7 @@
_trivial_GET()))
self.assertEquals(str(e), "Invalid response, no username given.")
+
@inlineCallbacks
def test_checkHash(self):
"""
@@ -332,20 +343,21 @@
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
-
+
self.failUnless(creds.checkHash(
md5('username:test realm:password').hexdigest()))
-
+
self.failIf(creds.checkHash(
md5('username:test realm:bogus').hexdigest()))
+
@inlineCallbacks
def test_invalidNonceCount(self):
"""
@@ -359,20 +371,20 @@
for ctr, factory in enumerate(credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse1 = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
clientResponse2 = authRequest2[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000002"),
)
-
+
yield factory.decode(clientResponse1, _trivial_GET())
yield factory.decode(clientResponse2, _trivial_GET())
-
+
if challenge.get('qop') is not None:
yield self.assertRaisesDeferred(
error.LoginFailed,
@@ -380,7 +392,7 @@
clientResponse2,
_trivial_GET()
)
-
+
challenge = (yield factory.getChallenge(clientAddress))
clientResponse1 = authRequest1[ctr] % (
@@ -400,6 +412,7 @@
_trivial_GET()
)
+
@inlineCallbacks
def test_invalidNonce(self):
"""
@@ -415,12 +428,12 @@
for ctr, factory in enumerate(credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
challenge['nonce'] = "noNoncense"
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
request = _trivial_GET()
yield self.assertRaisesDeferred(
error.LoginFailed,
@@ -431,11 +444,12 @@
factory._invalidate(FAKE_STATIC_NONCE)
response = (yield UnauthorizedResponse.makeResponse(
- {"Digest":factory},
+ {"Digest": factory},
request.remoteAddr
))
response.headers.getHeader("www-authenticate")[0][1]
+
@inlineCallbacks
def test_oldNonce(self):
"""
@@ -452,12 +466,12 @@
challenge = (yield factory.getChallenge(clientAddress))
nonce_count, timestamp = (yield factory.db.get(challenge['nonce']))
factory.db.set(challenge['nonce'], (nonce_count, timestamp - 2 * digest.DigestCredentialFactory.CHALLENGE_LIFETIME_SECS))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
request = _trivial_GET()
yield self.assertRaisesDeferred(
error.LoginFailed,
@@ -465,15 +479,16 @@
clientResponse,
request
)
-
+
response = (yield UnauthorizedResponse.makeResponse(
- {"Digest":factory},
+ {"Digest": factory},
request.remoteAddr,
))
wwwhdrs = response.headers.getHeader("www-authenticate")[0][1]
self.assertTrue('stale' in wwwhdrs, msg="No stale parameter in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
self.assertEquals(wwwhdrs['stale'], 'true', msg="stale parameter not set to true in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
+
def test_incompatibleCalcHA1Options(self):
"""
Test that the appropriate error is raised when any of the
@@ -500,6 +515,7 @@
preHA1=preHA1
)
+
@inlineCallbacks
def test_commaURI(self):
"""
@@ -508,15 +524,16 @@
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequestComma[ctr] % (
challenge['nonce'],
self.getDigestResponseComma(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
self.failUnless(creds.checkPassword('password'))
+
@inlineCallbacks
def test_stale_response(self):
"""
@@ -526,20 +543,20 @@
class newtime(object):
def time(self):
return theTime
- from twistedcaldav.directory import digest
- self.patch(digest, "time", newtime())
+ from twistedcaldav.directory import digest as ddigest
+ self.patch(ddigest, "time", newtime())
for ctr, factory in enumerate(self.credentialFactories):
challenge = (yield factory.getChallenge(clientAddress))
-
+
clientResponse = authRequest1[ctr] % (
challenge['nonce'],
self.getDigestResponse(challenge, "00000001"),
)
-
+
creds = (yield factory.decode(clientResponse, _trivial_GET()))
self.failUnless(creds.checkPassword('password'))
-
+
theTime += DigestCredentialFactory.CHALLENGE_LIFETIME_SECS + 1
request = _trivial_GET()
try:
@@ -555,8 +572,8 @@
self.fail("Invalid exception from nonce timeout: %s" % e)
challenge = (yield factory.getChallenge(request.remoteAddr))
self.assertTrue(challenge.get("stale") == "true")
-
+
+
def _trivial_GET():
return SimpleRequest(None, 'GET', '/')
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -121,7 +121,6 @@
self.count += 1
-
def test_expandedMembers(self):
"""
Make sure expandedMembers( ) returns a complete, flattened set of
@@ -889,7 +888,7 @@
}
members = pickle.loads(snapshotFile.getContent())
self.assertEquals(members, expected)
-
+
# "Corrupt" the snapshot and verify it is regenerated properly
snapshotFile.setContent("xyzzy")
cache.delete("group-cacher-populated")
@@ -900,8 +899,8 @@
self.assertTrue(snapshotFile.exists())
members = pickle.loads(snapshotFile.getContent())
self.assertEquals(members, expected)
-
+
def test_autoAcceptMembers(self):
"""
autoAcceptMembers( ) returns an empty list if no autoAcceptGroup is
@@ -926,6 +925,7 @@
])
)
+
@inlineCallbacks
def testScheduling(self):
"""
@@ -934,6 +934,7 @@
groupCacher = StubGroupCacher()
+
def decorateTransaction(txn):
txn._groupCacher = groupCacher
@@ -945,15 +946,19 @@
testScheduling.skip = "Fix WorkProposal to track delayed calls and cancel them"
+
+
class StubGroupCacher(object):
def __init__(self):
self.called = False
self.updateSeconds = 99
+
def updateCache(self):
self.called = True
+
class RecordsMatchingTokensTests(TestCase):
@inlineCallbacks
@@ -1038,6 +1043,7 @@
)
+
class GUIDTests(TestCase):
def setUp(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -52,7 +52,7 @@
newUID = "38D8AC00-5490-4425-BE3A-05FFB9862444"
homeResource = "/calendars/users/cdaboo/"
-
+
def privs1(result):
# Change GUID in record
self.xmlFile.setContent(
@@ -65,10 +65,10 @@
# Now force the calendar home resource to be reset
self.resetCalendars()
-
+
# Make sure new user cannot access old user's calendar home
return self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + newUID + "/"), davxml.Write, False)
-
+
# Make sure current user has access to their calendar home
d = self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + oldUID + "/"), davxml.Write, True)
d.addCallback(privs1)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -276,6 +276,7 @@
entry["expected"]
)
+
class StubList(object):
def __init__(self, wrapper):
self.ldap = wrapper
@@ -293,6 +294,7 @@
self.allResults = self.ldap.search_s(self.base, self.scope,
self.filterstr, attrlist=self.attrList)
+
class StubAsync(object):
def List(self, wrapper):
return StubList(wrapper)
@@ -304,7 +306,6 @@
whatever you have previously called addTestResults( ) with.
"""
-
def __init__(self, actual, records):
self.actual = actual
self.async = StubAsync()
@@ -587,9 +588,8 @@
"readOnlyProxyAttr": "read-only-proxy",
"autoAcceptGroupAttr": None,
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
- "partitionIdAttr": "partition-id", # maps to augments partition-id
},
}
)
@@ -803,9 +803,8 @@
"readOnlyProxyAttr": None,
"autoAcceptGroupAttr": None,
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
- "partitionIdAttr": "partition-id", # maps to augments partition-id
},
}
)
@@ -1021,9 +1020,8 @@
"readOnlyProxyAttr": None,
"autoAcceptGroupAttr": None,
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
- "partitionIdAttr": "partition-id", # maps to augments partition-id
},
}
)
@@ -1235,9 +1233,8 @@
"readOnlyProxyAttr": None,
"autoAcceptGroupAttr": None,
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
- "partitionIdAttr": "partition-id", # maps to augments partition-id
},
}
)
@@ -1295,7 +1292,6 @@
self.assertEquals(record.firstName, 'Amanda')
self.assertEquals(record.lastName, 'Test')
self.assertEquals(record.serverID, None)
- self.assertEquals(record.partitionID, None)
self.assertFalse(record.enabledForCalendaring)
# User with enabled-for-calendaring specified
@@ -1325,13 +1321,11 @@
'apple-generateduid': [guid],
'cn': ['Amanda Test'],
'server-id' : ["test-server-id"],
- 'partition-id' : ["test-partition-id"],
}
record = self.service._ldapResultToRecord(dn, attrs,
self.service.recordType_users)
self.assertEquals(record.serverID, "test-server-id")
- self.assertEquals(record.partitionID, "test-partition-id")
# User missing guidAttr
@@ -1471,7 +1465,6 @@
self.assertFalse(record.autoSchedule)
self.assertEquals(record.autoAcceptGroup, "")
-
# Now switch off the resourceInfoAttr and switch to individual
# attributes...
self.service.resourceSchema = {
@@ -1625,7 +1618,6 @@
self.assertFalse(self.service.isAllowedByRestrictToGroup(dn, attrs))
-
@inlineCallbacks
def test_groupMembershipAliases(self):
"""
@@ -1680,7 +1672,6 @@
)
-
def test_splitIntoBatches(self):
self.setupService(self.nestedUsingDifferentAttributeUsingDN)
# Data is perfect multiple of size
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -142,8 +142,6 @@
record = self.svc.recordWithShortName("users", "odtestcarlene")
self.assertTrue(record in records)
-
-
if runLDAPTests:
from twistedcaldav.directory.ldapdirectory import LdapDirectoryService
@@ -196,7 +194,6 @@
}
self.svc = LdapDirectoryService(params)
-
if runODTests:
from twistedcaldav.directory.appleopendirectory import OpenDirectoryService
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -47,6 +47,7 @@
augmentsFile = os.path.join(testRoot, "augments.xml")
config.AugmentService.params.xmlFiles = (augmentsFile,)
+
def test_createRecord(self):
directory = getDirectory()
@@ -75,6 +76,7 @@
record = directory.recordWithUID("location01")
self.assertNotEquals(record, None)
+
def test_destroyRecord(self):
directory = getDirectory()
@@ -95,6 +97,7 @@
record = directory.recordWithUID("location01")
self.assertNotEquals(record, None)
+
def test_updateRecord(self):
directory = getDirectory()
@@ -124,12 +127,14 @@
record = directory.recordWithUID("location01")
self.assertNotEquals(record, None)
+
def test_createDuplicateRecord(self):
directory = getDirectory()
directory.createRecord("resources", guid="resource01", shortNames=("resource01",), uid="resource01")
self.assertRaises(DirectoryError, directory.createRecord, "resources", guid="resource01", shortNames=("resource01",), uid="resource01")
+
def test_missingShortNames(self):
directory = getDirectory()
@@ -145,6 +150,7 @@
self.assertEquals(record.shortNames[0], "resource01")
self.assertEquals(record.fullName, "Resource #1")
+
def test_missingGUID(self):
directory = getDirectory()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,7 +21,20 @@
def test_multiplePhoneNumbersAndEmailAddresses(self):
- attributes={u'dsAttrTypeStandard:AppleMetaRecordName': ['uid=odtestamanda,cn=users,dc=dalek,dc=example,dc=com'], u'dsAttrTypeStandard:ModificationTimestamp': '20111017170937Z', u'dsAttrTypeStandard:PhoneNumber': ['408 555-1212', '415 555-1212'], u'dsAttrTypeStandard:RecordType': ['dsRecTypeStandard:Users'], u'dsAttrTypeStandard:AppleMetaNodeLocation': ['/LDAPv3/127.0.0.1'], u'dsAttrTypeStandard:RecordName': ['odtestamanda'], u'dsAttrTypeStandard:FirstName': 'Amanda', u'dsAttrTypeStandard:GeneratedUID': '9DC04A70-E6DD-11DF-9492-0800200C9A66', u'dsAttrTypeStandard:LastName': 'Test', u'dsAttrTypeStandard:CreationTimestamp': '20110927182945Z', u'dsAttrTypeStandard:EMailAddress': ['amanda at example.com', 'second at example.com'], u'dsAttrTypeStandard:RealName': 'Amanda Test'}
+ attributes = {
+ u'dsAttrTypeStandard:AppleMetaRecordName': ['uid=odtestamanda,cn=users,dc=dalek,dc=example,dc=com'],
+ u'dsAttrTypeStandard:ModificationTimestamp': '20111017170937Z',
+ u'dsAttrTypeStandard:PhoneNumber': ['408 555-1212', '415 555-1212'],
+ u'dsAttrTypeStandard:RecordType': ['dsRecTypeStandard:Users'],
+ u'dsAttrTypeStandard:AppleMetaNodeLocation': ['/LDAPv3/127.0.0.1'],
+ u'dsAttrTypeStandard:RecordName': ['odtestamanda'],
+ u'dsAttrTypeStandard:FirstName': 'Amanda',
+ u'dsAttrTypeStandard:GeneratedUID': '9DC04A70-E6DD-11DF-9492-0800200C9A66',
+ u'dsAttrTypeStandard:LastName': 'Test',
+ u'dsAttrTypeStandard:CreationTimestamp': '20110927182945Z',
+ u'dsAttrTypeStandard:EMailAddress': ['amanda at example.com', 'second at example.com'],
+ u'dsAttrTypeStandard:RealName': 'Amanda Test',
+ }
vcardRecord = VCardRecord(StubService(), attributes)
vcard = vcardRecord.vCard()
properties = set([prop.value() for prop in vcard.properties("TEL")])
@@ -30,6 +43,7 @@
self.assertEquals(properties, set(["amanda at example.com", "second at example.com"]))
+
class StubService(object):
addDSAttrXProperties = False
directoryBackedAddressBook = None
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -17,7 +17,7 @@
from twisted.internet.defer import inlineCallbacks
from twistedcaldav.config import config
from twistedcaldav.directory import calendaruserproxy
-from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB,\
+from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB, \
ProxyPostgreSQLDB
from twistedcaldav.directory.calendaruserproxyloader import XMLCalendarUserProxyLoader
import twistedcaldav.test.util
@@ -28,21 +28,21 @@
"""
Directory service provisioned principals.
"""
-
+
class old_ProxyDB(ProxySqliteDB):
-
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
return "3"
-
+
def _db_init_data_tables(self):
"""
Initialise the underlying database tables.
@param q: a database cursor to use.
"""
-
+
#
# GROUPS table
#
@@ -55,70 +55,77 @@
"""
)
+
class new_ProxyDB(ProxySqliteDB):
-
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
return "11"
-
+
+
class newer_ProxyDB(ProxySqliteDB):
-
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
return "51"
-
+
+
@inlineCallbacks
def test_normalDB(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
yield db.setGroupMembers("A", ("B", "C", "D",))
-
+
membersA = yield db.getMembers("A")
membershipsB = yield db.getMemberships("B")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membershipsB, set(("A",)))
+
@inlineCallbacks
def test_normalDBNonAscii(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
principalID = "Test \xe4\xbd\x90\xe8\x97\xa4"
yield db.setGroupMembers(principalID, ("B", "C", "D",))
-
+
membersA = yield db.getMembers(principalID)
membershipsB = yield db.getMemberships("B")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membershipsB, set((principalID,)))
+
@inlineCallbacks
def test_DBIndexed(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set(("GROUPNAMES", "MEMBERS")))
+
@inlineCallbacks
def test_OldDB(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = self.old_ProxyDB(db_path)
self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
+
@inlineCallbacks
def test_DBUpgrade(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = self.old_ProxyDB(db_path)
@@ -132,7 +139,7 @@
self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
db.close()
db = None
-
+
db = ProxySqliteDB(db_path)
membersA = yield db.getMembers("A")
@@ -144,9 +151,10 @@
db.close()
db = None
+
@inlineCallbacks
def test_DBUpgradeNewer(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = self.old_ProxyDB(db_path)
@@ -160,7 +168,7 @@
self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
db.close()
db = None
-
+
db = self.new_ProxyDB(db_path)
membersA = yield db.getMembers("A")
@@ -172,9 +180,10 @@
db.close()
db = None
+
@inlineCallbacks
def test_DBNoUpgradeNewer(self):
-
+
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = self.new_ProxyDB(db_path)
@@ -188,7 +197,7 @@
self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set(("GROUPNAMES", "MEMBERS")))
db.close()
db = None
-
+
db = self.newer_ProxyDB(db_path)
membersA = yield db.getMembers("A")
@@ -200,40 +209,41 @@
db.close()
db = None
+
@inlineCallbacks
def test_cachingDBInsert(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
-
+
# Do one insert and check the result
yield db.setGroupMembers("A", ("B", "C", "D",))
-
+
membersA = yield db.getMembers("A")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
membershipsE = yield db.getMemberships("E")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membershipsB, set(("A",)))
self.assertEqual(membershipsC, set(("A",)))
self.assertEqual(membershipsD, set(("A",)))
self.assertEqual(membershipsE, set(()))
-
+
# Change and check the result
yield db.setGroupMembers("A", ("B", "C", "E",))
-
+
membersA = yield db.getMembers("A")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
membershipsE = yield db.getMemberships("E")
-
+
self.assertEqual(membersA, set(("B", "C", "E",)))
self.assertEqual(membershipsB, set(("A",)))
self.assertEqual(membershipsC, set(("A",)))
@@ -242,41 +252,42 @@
yield db.clean()
+
@inlineCallbacks
def test_cachingDBRemove(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
-
+
# Do one insert and check the result
yield db.setGroupMembers("A", ("B", "C", "D",))
yield db.setGroupMembers("X", ("B", "C",))
-
+
membersA = yield db.getMembers("A")
membersX = yield db.getMembers("X")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set(("A", "X",)))
self.assertEqual(membershipsC, set(("A", "X",)))
self.assertEqual(membershipsD, set(("A",)))
-
+
# Remove and check the result
yield db.removeGroup("A")
-
+
membersA = yield db.getMembers("A")
membersX = yield db.getMembers("X")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
self.assertEqual(membersA, set())
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set("X",))
@@ -285,33 +296,34 @@
yield db.clean()
+
@inlineCallbacks
def test_cachingDBRemoveSpecial(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
-
+
# Do one insert and check the result
yield db.setGroupMembers("A", ("B", "C", "D",))
yield db.setGroupMembers("X", ("B", "C",))
-
+
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
# Remove and check the result
yield db.removeGroup("A")
-
+
membersA = yield db.getMembers("A")
membersX = yield db.getMembers("X")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
self.assertEqual(membersA, set())
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set("X",))
@@ -320,41 +332,42 @@
yield db.clean()
+
@inlineCallbacks
def test_cachingDBRemovePrincipal(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
-
+
# Do one insert and check the result
yield db.setGroupMembers("A", ("B", "C", "D",))
yield db.setGroupMembers("X", ("B", "C",))
-
+
membersA = yield db.getMembers("A")
membersX = yield db.getMembers("X")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set(("A", "X",)))
self.assertEqual(membershipsC, set(("A", "X",)))
self.assertEqual(membershipsD, set(("A",)))
-
+
# Remove and check the result
yield db.removePrincipal("B")
-
+
membersA = yield db.getMembers("A")
membersX = yield db.getMembers("X")
membershipsB = yield db.getMemberships("B")
membershipsC = yield db.getMemberships("C")
membershipsD = yield db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("C", "D",)))
self.assertEqual(membersX, set(("C",)))
self.assertEqual(membershipsB, set())
@@ -363,29 +376,30 @@
yield db.clean()
+
@inlineCallbacks
def test_cachingDBInsertUncached(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
db_path = os.path.abspath(self.mktemp())
db = ProxySqliteDB(db_path)
-
+
# Do one insert and check the result for the one we will remove
yield db.setGroupMembers("AA", ("BB", "CC", "DD",))
yield db.getMemberships("DD")
-
+
# Change and check the result
yield db.setGroupMembers("AA", ("BB", "CC", "EE",))
-
+
membersAA = yield db.getMembers("AA")
membershipsBB = yield db.getMemberships("BB")
membershipsCC = yield db.getMemberships("CC")
membershipsDD = yield db.getMemberships("DD")
membershipsEE = yield db.getMemberships("EE")
-
+
self.assertEqual(membersAA, set(("BB", "CC", "EE",)))
self.assertEqual(membershipsBB, set(("AA",)))
self.assertEqual(membershipsCC, set(("AA",)))
@@ -394,11 +408,13 @@
yield db.clean()
+
+
class ProxyPrincipalDBPostgreSQL (twistedcaldav.test.util.TestCase):
"""
Directory service provisioned principals.
"""
-
+
@inlineCallbacks
def setUp(self):
@@ -406,209 +422,217 @@
self.db = ProxyPostgreSQLDB(host="localhost", database="proxies")
yield self.db.clean()
+
@inlineCallbacks
def tearDown(self):
yield self.db.close()
self.db = None
+
@inlineCallbacks
def test_normalDB(self):
-
+
# Get the DB
yield self.db.clean()
-
+
calendaruserproxy.ProxyDBService = self.db
loader = XMLCalendarUserProxyLoader("/Volumes/Data/Users/cyrusdaboo/Documents/Development/Apple/eclipse/CalendarServer-3/conf/auth/proxies-test.xml")
yield loader.updateProxyDB()
yield self.db.setGroupMembers("A", ("B", "C", "D",))
-
+
membersA = yield self.db.getMembers("A")
membershipsB = yield self.db.getMemberships("B")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membershipsB, set(("A",)))
+
@inlineCallbacks
def test_DBIndexed(self):
-
+
# Get the DB
yield self.db.clean()
self.assertTrue((yield self.db.queryOne("select hasindexes from pg_tables where tablename = 'groups'")))
+
@inlineCallbacks
def test_cachingDBInsert(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
yield self.db.clean()
-
+
# Do one insert and check the result
yield self.db.setGroupMembers("A", ("B", "C", "D",))
-
+
membersA = yield self.db.getMembers("A")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
membershipsE = yield self.db.getMemberships("E")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membershipsB, set(("A",)))
self.assertEqual(membershipsC, set(("A",)))
self.assertEqual(membershipsD, set(("A",)))
self.assertEqual(membershipsE, set(()))
-
+
# Change and check the result
yield self.db.setGroupMembers("A", ("B", "C", "E",))
-
+
membersA = yield self.db.getMembers("A")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
membershipsE = yield self.db.getMemberships("E")
-
+
self.assertEqual(membersA, set(("B", "C", "E",)))
self.assertEqual(membershipsB, set(("A",)))
self.assertEqual(membershipsC, set(("A",)))
self.assertEqual(membershipsD, set())
self.assertEqual(membershipsE, set(("A",)))
+
@inlineCallbacks
def test_cachingDBRemove(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
yield self.db.clean()
-
+
# Do one insert and check the result
yield self.db.setGroupMembers("A", ("B", "C", "D",))
yield self.db.setGroupMembers("X", ("B", "C",))
-
+
membersA = yield self.db.getMembers("A")
membersX = yield self.db.getMembers("X")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set(("A", "X",)))
self.assertEqual(membershipsC, set(("A", "X",)))
self.assertEqual(membershipsD, set(("A",)))
-
+
# Remove and check the result
yield self.db.removeGroup("A")
-
+
membersA = yield self.db.getMembers("A")
membersX = yield self.db.getMembers("X")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
self.assertEqual(membersA, set())
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set("X",))
self.assertEqual(membershipsC, set("X",))
self.assertEqual(membershipsD, set())
+
@inlineCallbacks
def test_cachingDBRemoveSpecial(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
yield self.db.clean()
-
+
# Do one insert and check the result
yield self.db.setGroupMembers("A", ("B", "C", "D",))
yield self.db.setGroupMembers("X", ("B", "C",))
-
+
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
# Remove and check the result
yield self.db.removeGroup("A")
-
+
membersA = yield self.db.getMembers("A")
membersX = yield self.db.getMembers("X")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
self.assertEqual(membersA, set())
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set("X",))
self.assertEqual(membershipsC, set("X",))
self.assertEqual(membershipsD, set())
+
@inlineCallbacks
def test_cachingDBRemovePrincipal(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
yield self.db.clean()
-
+
# Do one insert and check the result
yield self.db.setGroupMembers("A", ("B", "C", "D",))
yield self.db.setGroupMembers("X", ("B", "C",))
-
+
membersA = yield self.db.getMembers("A")
membersX = yield self.db.getMembers("X")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("B", "C", "D",)))
self.assertEqual(membersX, set(("B", "C",)))
self.assertEqual(membershipsB, set(("A", "X",)))
self.assertEqual(membershipsC, set(("A", "X",)))
self.assertEqual(membershipsD, set(("A",)))
-
+
# Remove and check the result
yield self.db.removePrincipal("B")
-
+
membersA = yield self.db.getMembers("A")
membersX = yield self.db.getMembers("X")
membershipsB = yield self.db.getMemberships("B")
membershipsC = yield self.db.getMemberships("C")
membershipsD = yield self.db.getMemberships("D")
-
+
self.assertEqual(membersA, set(("C", "D",)))
self.assertEqual(membersX, set(("C",)))
self.assertEqual(membershipsB, set())
self.assertEqual(membershipsC, set(("A", "X",)))
self.assertEqual(membershipsD, set(("A",),))
+
@inlineCallbacks
def test_cachingDBInsertUncached(self):
-
+
for processType in ("Single", "Combined",):
config.ProcessType = processType
# Get the DB
yield self.db.clean()
-
+
# Do one insert and check the result for the one we will remove
yield self.db.setGroupMembers("AA", ("BB", "CC", "DD",))
yield self.db.getMemberships("DD")
-
+
# Change and check the result
yield self.db.setGroupMembers("AA", ("BB", "CC", "EE",))
-
+
membersAA = yield self.db.getMembers("AA")
membershipsBB = yield self.db.getMemberships("BB")
membershipsCC = yield self.db.getMemberships("CC")
membershipsDD = yield self.db.getMemberships("DD")
membershipsEE = yield self.db.getMemberships("EE")
-
+
self.assertEqual(membersAA, set(("BB", "CC", "EE",)))
self.assertEqual(membershipsBB, set(("AA",)))
self.assertEqual(membershipsCC, set(("AA",)))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -41,6 +41,7 @@
# def test_loadConfig(self):
# directory = getDirectory()
+
def test_recordInPrimaryDirectory(self):
directory = getDirectory()
@@ -48,6 +49,7 @@
record = directory.recordWithUID("user01")
self.assertNotEquals(record, None)
+
def test_recordInSupplementalDirectory(self):
directory = getDirectory()
@@ -55,6 +57,7 @@
record = directory.recordWithUID("resource01")
self.assertNotEquals(record, None)
+
def test_augments(self):
directory = getDirectory()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -38,49 +38,50 @@
))
users = {
- "admin" : { "password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": () },
- "wsanchez" : { "password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez at example.com",) },
- "cdaboo" : { "password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo at example.com",) },
- "lecroy" : { "password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy at example.com",) },
- "dreid" : { "password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid at example.com",) },
- "nocalendar" : { "password": "radnelacon", "guid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "addresses": () },
- "user01" : { "password": "01user", "guid": None , "addresses": ("mailto:c4ca4238a0 at example.com",) },
- "user02" : { "password": "02user", "guid": None , "addresses": ("mailto:c81e728d9d at example.com",) },
- }
+ "admin" : {"password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": ()},
+ "wsanchez" : {"password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez at example.com",)},
+ "cdaboo" : {"password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo at example.com",) },
+ "lecroy" : {"password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy at example.com",) },
+ "dreid" : {"password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid at example.com",) },
+ "nocalendar" : {"password": "radnelacon", "guid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "addresses": ()},
+ "user01" : {"password": "01user", "guid": None, "addresses": ("mailto:c4ca4238a0 at example.com",)},
+ "user02" : {"password": "02user", "guid": None, "addresses": ("mailto:c81e728d9d at example.com",)},
+ }
groups = {
- "admin" : { "password": "admin", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "managers"),)},
- "managers" : { "password": "managers", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "lecroy"),)},
- "grunts" : { "password": "grunts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
+ "admin" : {"password": "admin", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "managers"),)},
+ "managers" : {"password": "managers", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "lecroy"),)},
+ "grunts" : {"password": "grunts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
(DirectoryService.recordType_users , "cdaboo"),
(DirectoryService.recordType_users , "dreid"))},
- "right_coast": { "password": "right_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),)},
- "left_coast" : { "password": "left_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
+ "right_coast": {"password": "right_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),)},
+ "left_coast" : {"password": "left_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
(DirectoryService.recordType_users , "dreid"),
(DirectoryService.recordType_users , "lecroy"))},
- "both_coasts": { "password": "both_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "right_coast"),
+ "both_coasts": {"password": "both_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "right_coast"),
(DirectoryService.recordType_groups, "left_coast"))},
- "recursive1_coasts": { "password": "recursive1_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive2_coasts"),
+ "recursive1_coasts": {"password": "recursive1_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive2_coasts"),
(DirectoryService.recordType_users, "wsanchez"))},
- "recursive2_coasts": { "password": "recursive2_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive1_coasts"),
+ "recursive2_coasts": {"password": "recursive2_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive1_coasts"),
(DirectoryService.recordType_users, "cdaboo"))},
- "non_calendar_group": { "password": "non_calendar_group", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),
+ "non_calendar_group": {"password": "non_calendar_group", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),
(DirectoryService.recordType_users , "lecroy"))},
- }
+ }
locations = {
- "mercury": { "password": "mercury", "guid": None, "addresses": ("mailto:mercury at example.com",) },
- "gemini" : { "password": "gemini", "guid": None, "addresses": ("mailto:gemini at example.com",) },
- "apollo" : { "password": "apollo", "guid": None, "addresses": ("mailto:apollo at example.com",) },
- "orion" : { "password": "orion", "guid": None, "addresses": ("mailto:orion at example.com",) },
- }
+ "mercury": {"password": "mercury", "guid": None, "addresses": ("mailto:mercury at example.com",)},
+ "gemini" : {"password": "gemini", "guid": None, "addresses": ("mailto:gemini at example.com",)},
+ "apollo" : {"password": "apollo", "guid": None, "addresses": ("mailto:apollo at example.com",)},
+ "orion" : {"password": "orion", "guid": None, "addresses": ("mailto:orion at example.com",)},
+ }
resources = {
- "transporter" : { "password": "transporter", "guid": None, "addresses": ("mailto:transporter at example.com",) },
- "ftlcpu" : { "password": "ftlcpu", "guid": None, "addresses": ("mailto:ftlcpu at example.com",) },
- "non_calendar_proxy" : { "password": "non_calendar_proxy", "guid": "non_calendar_proxy", "addresses": ("mailto:non_calendar_proxy at example.com",) },
- }
+ "transporter" : {"password": "transporter", "guid": None, "addresses": ("mailto:transporter at example.com",) },
+ "ftlcpu" : {"password": "ftlcpu", "guid": None, "addresses": ("mailto:ftlcpu at example.com",) },
+ "non_calendar_proxy" : {"password": "non_calendar_proxy", "guid": "non_calendar_proxy", "addresses": ("mailto:non_calendar_proxy at example.com",)},
+ }
+
def xmlFile(self):
"""
Create a L{FilePath} that points to a temporary file containing a copy
@@ -155,10 +156,10 @@
"""
)
for recordType, expectedRecords in (
- ( DirectoryService.recordType_users , ("admin",) ),
- ( DirectoryService.recordType_groups , () ),
- ( DirectoryService.recordType_locations , () ),
- ( DirectoryService.recordType_resources , () ),
+ (DirectoryService.recordType_users , ("admin",)),
+ (DirectoryService.recordType_groups , ()),
+ (DirectoryService.recordType_locations , ()),
+ (DirectoryService.recordType_resources , ()),
):
# Fault records in
for name in expectedRecords:
@@ -169,6 +170,7 @@
set(expectedRecords)
)
+
def test_okAutoSchedule(self):
service = self.service()
@@ -201,10 +203,10 @@
service.augmentService.refresh()
for recordType, expectedRecords in (
- ( DirectoryService.recordType_users , () ),
- ( DirectoryService.recordType_groups , () ),
- ( DirectoryService.recordType_locations , ("my office",) ),
- ( DirectoryService.recordType_resources , () ),
+ (DirectoryService.recordType_users , ()),
+ (DirectoryService.recordType_groups , ()),
+ (DirectoryService.recordType_locations , ("my office",)),
+ (DirectoryService.recordType_resources , ()),
):
# Fault records in
for name in expectedRecords:
@@ -239,12 +241,12 @@
</accounts>
"""
)
-
+
for recordType, expectedRecords in (
- ( DirectoryService.recordType_users , () ),
- ( DirectoryService.recordType_groups , ("enabled", "disabled") ),
- ( DirectoryService.recordType_locations , () ),
- ( DirectoryService.recordType_resources , () ),
+ (DirectoryService.recordType_users , ()),
+ (DirectoryService.recordType_groups , ("enabled", "disabled")),
+ (DirectoryService.recordType_locations , ()),
+ (DirectoryService.recordType_resources , ()),
):
# Fault records in
for name in expectedRecords:
@@ -279,13 +281,14 @@
</accounts>
"""
)
-
+
record = service.recordWithShortName(
DirectoryService.recordType_locations, "my office")
self.assertEquals(record.guid, "myoffice")
self.assertEquals(record.extras["comment"], "This is the comment")
self.assertEquals(record.extras["capacity"], "40")
+
def test_writeExtras(self):
service = self.service()
@@ -302,7 +305,6 @@
self.assertEquals(record.extras["comment"], "Test comment")
self.assertEquals(record.extras["capacity"], "10")
-
service.updateRecord(DirectoryService.recordType_locations, "newguid",
shortNames=("New office",),
fullName="My Newer Office",
@@ -333,6 +335,7 @@
self.assertNotEquals(None, service._lookupInIndex(service.recordType_locations, service.INDEX_TYPE_SHORTNAME, "orion"))
self.assertEquals(None, service._lookupInIndex(service.recordType_users, service.INDEX_TYPE_CUA, "mailto:nobody at example.com"))
+
def test_repeat(self):
service = self.service()
record = service.recordWithShortName(
@@ -342,6 +345,8 @@
self.assertEquals(record.lastName, "c4ca4238a User 01")
self.assertEquals(record.emailAddresses, set(['c4ca4238a0 at example.com']))
+
+
class XMLFileSubset (XMLFileBase, TestCase):
"""
Test the recordTypes subset feature of XMLFile service.
@@ -351,6 +356,7 @@
DirectoryService.recordType_groups,
))
+
def test_recordTypesSubset(self):
directory = XMLDirectoryService(
{
@@ -366,4 +372,3 @@
alwaysStat=True
)
self.assertEquals(set(("users", "groups")), set(directory.recordTypes()))
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -54,12 +54,14 @@
# For aggregator subclasses
recordTypePrefixes = ("",)
+
def test_realm(self):
"""
IDirectoryService.realm
"""
self.failUnless(self.service().realmName)
+
def test_recordTypes(self):
"""
IDirectoryService.recordTypes()
@@ -69,15 +71,16 @@
self.assertEquals(set(self.service().recordTypes()), self.recordTypes)
+
def test_recordWithShortName(self):
"""
IDirectoryService.recordWithShortName()
"""
for recordType, data in (
- ( DirectoryService.recordType_users , self.users ),
- ( DirectoryService.recordType_groups , self.groups ),
- ( DirectoryService.recordType_locations, self.locations ),
- ( DirectoryService.recordType_resources, self.resources ),
+ (DirectoryService.recordType_users , self.users),
+ (DirectoryService.recordType_groups , self.groups),
+ (DirectoryService.recordType_locations, self.locations),
+ (DirectoryService.recordType_resources, self.resources),
):
if not data:
raise SkipTest("No %s" % (recordType,))
@@ -95,6 +98,7 @@
continue
self.assertEquals(record, None)
+
def test_recordWithUID(self):
service = self.service()
record = None
@@ -108,6 +112,7 @@
if record is None:
raise SkipTest("No GUIDs provided to test")
+
def test_recordWithCalendarUserAddress(self):
service = self.service()
record = None
@@ -120,6 +125,7 @@
if record is None:
raise SkipTest("No calendar user addresses provided to test")
+
def test_groupMembers(self):
"""
IDirectoryRecord.members()
@@ -138,6 +144,7 @@
"Wrong membership for group %r: %s != %s" % (group, result, expected)
)
+
def test_groupMemberships(self):
"""
IDirectoryRecord.groups()
@@ -148,8 +155,8 @@
raise SkipTest("No groups")
for recordType, data in (
- ( DirectoryService.recordType_users , self.users ),
- ( DirectoryService.recordType_groups, self.groups ),
+ (DirectoryService.recordType_users , self.users),
+ (DirectoryService.recordType_groups, self.groups),
):
service = self.service()
for shortName, info in data.iteritems():
@@ -162,6 +169,7 @@
"Wrong groups for %s %r: %s != %s" % (record.recordType, shortName, result, expected)
)
+
def recordNames(self, recordType):
service = self.service()
names = set()
@@ -176,16 +184,18 @@
return names
+
def allEntries(self):
for data, _ignore_recordType in (
- (self.users, DirectoryService.recordType_users ),
- (self.groups, DirectoryService.recordType_groups ),
+ (self.users, DirectoryService.recordType_users),
+ (self.groups, DirectoryService.recordType_groups),
(self.locations, DirectoryService.recordType_locations),
(self.resources, DirectoryService.recordType_resources),
):
for item in data.iteritems():
yield item
+
def compare(self, record, shortName, data):
def value(key):
if key in data:
@@ -217,6 +227,7 @@
if value("name"):
self.assertEquals(record.fullName, value("name"))
+
def servicePrefix(self):
service = self.service()
if hasattr(service, "recordTypePrefix"):
@@ -224,6 +235,8 @@
else:
return ""
+
+
class NonCachingTestCase (DirectoryTestCase):
def test_listRecords_user(self):
@@ -235,6 +248,7 @@
self.assertEquals(self.recordNames(DirectoryService.recordType_users), set(self.users.keys()))
+
def test_listRecords_group(self):
"""
IDirectoryService.listRecords(DirectoryService.recordType_groups)
@@ -244,6 +258,7 @@
self.assertEquals(self.recordNames(DirectoryService.recordType_groups), set(self.groups.keys()))
+
def test_listRecords_locations(self):
"""
IDirectoryService.listRecords("locations")
@@ -253,6 +268,7 @@
self.assertEquals(self.recordNames(DirectoryService.recordType_locations), set(self.locations.keys()))
+
def test_listRecords_resources(self):
"""
IDirectoryService.listRecords("resources")
@@ -263,6 +279,7 @@
self.assertEquals(self.recordNames(DirectoryService.recordType_resources), set(self.resources.keys()))
+
class BasicTestCase (DirectoryTestCase):
"""
Tests a directory implementation with basic auth.
@@ -279,6 +296,8 @@
userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
self.failUnless(userRecord.verifyCredentials(UsernamePassword(user, self.users[user]["password"])))
+
+
# authRequest = {
# username="username",
# realm="test realm",
@@ -351,6 +370,8 @@
else:
self.failIf(userRecord.verifyCredentials(credentials))
+
+
def maybeCommit(req):
class JustForCleanup(object):
def newTransaction(self, *whatever):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,38 +30,36 @@
log = Logger()
-ELEMENT_AUGMENTS = "augments"
-ELEMENT_RECORD = "record"
+ELEMENT_AUGMENTS = "augments"
+ELEMENT_RECORD = "record"
-ELEMENT_UID = "uid"
-ELEMENT_ENABLE = "enable"
-ELEMENT_SERVERID = "server-id"
-ELEMENT_PARTITIONID = "partition-id"
-ELEMENT_HOSTEDAT = "hosted-at" # Backwards compatibility
-ELEMENT_ENABLECALENDAR = "enable-calendar"
+ELEMENT_UID = "uid"
+ELEMENT_ENABLE = "enable"
+ELEMENT_SERVERID = "server-id"
+ELEMENT_PARTITIONID = "partition-id" # Backwards compatibility
+ELEMENT_HOSTEDAT = "hosted-at" # Backwards compatibility
+ELEMENT_ENABLECALENDAR = "enable-calendar"
ELEMENT_ENABLEADDRESSBOOK = "enable-addressbook"
-ELEMENT_ENABLELOGIN = "enable-login"
-ELEMENT_AUTOSCHEDULE = "auto-schedule"
+ELEMENT_ENABLELOGIN = "enable-login"
+ELEMENT_AUTOSCHEDULE = "auto-schedule"
ELEMENT_AUTOSCHEDULE_MODE = "auto-schedule-mode"
-ELEMENT_AUTOACCEPTGROUP = "auto-accept-group"
+ELEMENT_AUTOACCEPTGROUP = "auto-accept-group"
-ATTRIBUTE_REPEAT = "repeat"
+ATTRIBUTE_REPEAT = "repeat"
-VALUE_TRUE = "true"
-VALUE_FALSE = "false"
+VALUE_TRUE = "true"
+VALUE_FALSE = "false"
ELEMENT_AUGMENTRECORD_MAP = {
- ELEMENT_UID: "uid",
- ELEMENT_ENABLE: "enabled",
- ELEMENT_SERVERID: "serverID",
- ELEMENT_PARTITIONID: "partitionID",
- ELEMENT_HOSTEDAT: "partitionID", # Backwards compatibility
- ELEMENT_ENABLECALENDAR: "enabledForCalendaring",
+ ELEMENT_UID: "uid",
+ ELEMENT_ENABLE: "enabled",
+ ELEMENT_SERVERID: "serverID",
+ ELEMENT_ENABLECALENDAR: "enabledForCalendaring",
ELEMENT_ENABLEADDRESSBOOK: "enabledForAddressBooks",
- ELEMENT_ENABLELOGIN: "enabledForLogin",
- ELEMENT_AUTOSCHEDULE: "autoSchedule",
+ ELEMENT_ENABLELOGIN: "enabledForLogin",
+ ELEMENT_AUTOSCHEDULE: "autoSchedule",
ELEMENT_AUTOSCHEDULE_MODE: "autoScheduleMode",
- ELEMENT_AUTOACCEPTGROUP: "autoAcceptGroup",
+ ELEMENT_AUTOACCEPTGROUP: "autoAcceptGroup",
}
class XMLAugmentsParser(object):
@@ -71,6 +69,7 @@
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
+
def __init__(self, xmlFile, items):
self.items = items
@@ -84,13 +83,14 @@
self._parseXML(augments_node)
+
def _parseXML(self, rootnode):
"""
Parse the XML root node from the augments configuration document.
@param rootnode: the L{Element} to parse.
"""
for child in rootnode:
-
+
if child.tag != ELEMENT_RECORD:
raise RuntimeError("Unknown augment type: '%s' in augment file: '%s'" % (child.tag, self.xmlFile,))
@@ -98,7 +98,7 @@
fields = {}
for node in child:
-
+
if node.tag in (
ELEMENT_UID,
ELEMENT_SERVERID,
@@ -118,33 +118,35 @@
fields[node.tag] = node.text == VALUE_TRUE
else:
raise RuntimeError("Invalid element '%s' in augment file: '%s'" % (node.tag, self.xmlFile,))
-
+
# Must have at least a uid
if ELEMENT_UID not in fields:
raise RuntimeError("Invalid record '%s' without a uid in augment file: '%s'" % (child, self.xmlFile,))
-
+
if repeat > 1:
- for i in xrange(1, repeat+1):
+ for i in xrange(1, repeat + 1):
self.buildRecord(fields, i)
else:
self.buildRecord(fields)
-
+
+
def buildRecord(self, fields, count=None):
-
+
from twistedcaldav.directory.augment import AugmentRecord
def expandCount(value, count):
-
+
if type(value) in types.StringTypes:
return value % (count,) if count and "%" in value else value
elif type(value) == set:
return set([item % (count,) if count and "%" in item else item for item in value])
else:
return value
-
+
actualFields = {}
- for k,v in fields.iteritems():
- actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
+ for k, v in fields.iteritems():
+ if k in ELEMENT_AUGMENTRECORD_MAP:
+ actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
record = AugmentRecord(**actualFields)
self.items[record.uid] = record
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -44,12 +44,15 @@
def resourceType(self):
return davxml.ResourceType.dropboxhome #@UndefinedVariable
+
def isCollection(self):
return True
+
def http_PUT(self, request):
return responsecode.FORBIDDEN
+
@inlineCallbacks
def accessControlList(self, request, *args, **kwargs):
"""
@@ -57,7 +60,7 @@
"""
acl = (yield super(DropBoxHomeResource, self).accessControlList(request, *args, **kwargs))
-
+
if config.EnableProxyPrincipals:
owner = (yield self.ownerPrincipal(request))
@@ -75,10 +78,12 @@
)
returnValue(davxml.ACL(*newaces))
-
+
else:
returnValue(acl)
+
+
class DropBoxCollectionResource (DAVResource):
"""
Drop box resource.
@@ -99,7 +104,7 @@
calendar collection have the same privileges unless explicitly overridden. The same applies
to drop box collections as we want all resources (attachments) to have the same privileges as
the drop box collection.
-
+
@param newaces: C{list} of L{ACE} for ACL being set.
"""
# Add inheritable option to each ACE in the list
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -494,13 +494,13 @@
size = child.contentLength()
lastModified = child.lastModified()
rtypes = []
- fullrtype = child.resourceType()
+ fullrtype = child.resourceType() if hasattr(child, "resourceType") else None
if fullrtype is not None:
for rtype in fullrtype.children:
rtypes.append(rtype.name)
if rtypes:
rtypes = "(%s)" % (", ".join(rtypes),)
- if child.isCollection():
+ if child.isCollection() if hasattr(child, "isCollection") else False:
contentType = rtypes
else:
mimeType = child.contentType()
@@ -1004,6 +1004,7 @@
return tokens, context, applyTo, clientLimit, propElement
+
def validateTokens(tokens):
"""
Make sure there is at least one token longer than one character
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -47,8 +47,8 @@
from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
from txdav.caldav.datastore.scheduling.scheduler import Scheduler
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
log = Logger()
@@ -189,15 +189,15 @@
# Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
try:
if self.start:
- self.start = PyCalendarDateTime.parseText(self.start)
+ self.start = DateTime.parseText(self.start)
if not self.start.utc():
raise ValueError()
if self.end:
- self.end = PyCalendarDateTime.parseText(self.end)
+ self.end = DateTime.parseText(self.end)
if not self.end.utc():
raise ValueError()
if self.duration:
- self.duration = PyCalendarDuration.parseText(self.duration)
+ self.duration = Duration.parseText(self.duration)
except ValueError:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
@@ -225,12 +225,12 @@
# Now fill in the missing pieces
if self.start is None:
- self.start = PyCalendarDateTime.getNowUTC()
+ self.start = DateTime.getNowUTC()
self.start.setHHMMSS(0, 0, 0)
if self.duration:
self.end = self.start + self.duration
if self.end is None:
- self.end = self.start + PyCalendarDuration(days=config.FreeBusyURL.TimePeriod)
+ self.end = self.start + Duration(days=config.FreeBusyURL.TimePeriod)
# End > start
if self.end <= self.start:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -28,7 +28,6 @@
"tzexpand",
]
-import cStringIO as StringIO
import codecs
from difflib import unified_diff
import heapq
@@ -46,31 +45,34 @@
from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
from twistedcaldav.timezones import hasTZ, TimezoneException
-from pycalendar import definitions
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.calendar import PyCalendar
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.exceptions import PyCalendarError
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.property import PyCalendarProperty
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
+from pycalendar.icalendar import definitions
+from pycalendar.parameter import Parameter
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.icalendar.component import Component as PyComponent
+from pycalendar.componentbase import ComponentBase
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.exceptions import ErrorBase
+from pycalendar.period import Period
+from pycalendar.icalendar.property import Property as PyProperty
+from pycalendar.timezone import Timezone
+from pycalendar.utcoffsetvalue import UTCOffsetValue
log = Logger()
iCalendarProductID = "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"
-allowedComponents = (
- "VEVENT",
- "VTODO",
- "VTIMEZONE",
- # "VJOURNAL",
- "VFREEBUSY",
- # "VAVAILABILITY",
-)
+allowedStoreComponents = ("VEVENT", "VTODO", "VPOLL",)
+allowedSchedulingComponents = allowedStoreComponents + ("VFREEBUSY",)
+allowedComponents = allowedSchedulingComponents + ("VTIMEZONE",)
+def _updateAllowedComponents(allowed):
+ global allowedStoreComponents, allowedSchedulingComponents, allowedComponents
+ allowedStoreComponents = allowed
+ allowedSchedulingComponents = allowedStoreComponents + ("VFREEBUSY",)
+ allowedComponents = allowedSchedulingComponents + ("VTIMEZONE",)
+
+
# Additional per-user data components - see datafilters.peruserdata.py for details
PERUSER_COMPONENT = "X-CALENDARSERVER-PERUSER"
PERUSER_UID = "X-CALENDARSERVER-PERUSER-UID"
@@ -134,19 +136,28 @@
"LAST-MODIFIED": (None, {"VALUE": "DATE-TIME"}),
"SEQUENCE": (0, {"VALUE": "INTEGER"}),
"REQUEST-STATUS": (None, {"VALUE": "TEXT"}),
+
+ "VOTER": (None, {
+ "VALUE": "CAL-ADDRESS",
+ "CUTYPE": "INDIVIDUAL",
+ "ROLE": "REQ-PARTICIPANT",
+ "RSVP": "FALSE",
+ "SCHEDULE-AGENT": "SERVER",
+ }),
}
# transformations to apply to property values
normalizePropsValue = {
"ATTENDEE": normalizeCUAddr,
"ORGANIZER": normalizeCUAddr,
+ "VOTER": normalizeCUAddr,
}
ignoredComponents = ("VTIMEZONE", PERUSER_COMPONENT,)
# Used for min/max time-range query limits
-minDateTime = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
-maxDateTime = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+minDateTime = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+maxDateTime = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
class InvalidICalendarDataError(ValueError):
pass
@@ -170,16 +181,16 @@
pyobj = kwargs["pycalendar"]
- if not isinstance(pyobj, PyCalendarProperty):
- raise TypeError("Not a PyCalendarProperty: %r" % (property,))
+ if not isinstance(pyobj, PyProperty):
+ raise TypeError("Not a Property: %r" % (property,))
self._pycalendar = pyobj
else:
# Convert params dictionary to list of lists format used by pycalendar
valuetype = kwargs.get("valuetype")
- self._pycalendar = PyCalendarProperty(name, value, valuetype=valuetype)
+ self._pycalendar = PyProperty(name, value, valuetype=valuetype)
for attrname, attrvalue in params.items():
- self._pycalendar.addAttribute(PyCalendarAttribute(attrname, attrvalue))
+ self._pycalendar.addParameter(Parameter(attrname, attrvalue))
self._parent = parent
@@ -267,7 +278,7 @@
Returns a set containing parameter names for this property.
"""
result = set()
- for pyattrlist in self._pycalendar.getAttributes().values():
+ for pyattrlist in self._pycalendar.getParameters().values():
for pyattr in pyattrlist:
result.add(pyattr.getName())
return result
@@ -275,31 +286,40 @@
def parameterValue(self, name, default=None):
"""
- Returns a single value for the given parameter. Raises
- InvalidICalendarDataError if the parameter has more than one value.
+ Returns a single value for the given parameter.
"""
try:
- return self._pycalendar.getAttributeValue(name)
+ return self._pycalendar.getParameterValue(name)
except KeyError:
return default
+ def parameterValues(self, name, default=None):
+ """
+ Returns a multi-value C{list} for the given parameter.
+ """
+ try:
+ return self._pycalendar.getParameterValues(name)
+ except KeyError:
+ return default
+
+
def hasParameter(self, paramname):
- return self._pycalendar.hasAttribute(paramname)
+ return self._pycalendar.hasParameter(paramname)
def setParameter(self, paramname, paramvalue):
- self._pycalendar.replaceAttribute(PyCalendarAttribute(paramname, paramvalue))
+ self._pycalendar.replaceParameter(Parameter(paramname, paramvalue))
self._markAsDirty()
def removeParameter(self, paramname):
- self._pycalendar.removeAttributes(paramname)
+ self._pycalendar.removeParameters(paramname)
self._markAsDirty()
def removeAllParameters(self):
- self._pycalendar.setAttributes({})
+ self._pycalendar.setParameters({})
self._markAsDirty()
@@ -308,11 +328,11 @@
paramname = paramname.upper()
for attrName in self.parameterNames():
if attrName.upper() == paramname:
- for attr in tuple(self._pycalendar.getAttributes()[attrName]):
+ for attr in tuple(self._pycalendar.getParameters()[attrName]):
for value in attr.getValues():
if value == paramvalue:
if not attr.removeValue(value):
- self._pycalendar.removeAttributes(paramname)
+ self._pycalendar.removeParameters(paramname)
self._markAsDirty()
@@ -322,8 +342,8 @@
start/end period.
The only properties allowed for this query are: COMPLETED, CREATED, DTSTAMP and
LAST-MODIFIED (caldav -09).
- @param start: a L{PyCalendarDateTime} specifying the beginning of the given time span.
- @param end: a L{PyCalendarDateTime} specifying the end of the given time span.
+ @param start: a L{DateTime} specifying the beginning of the given time span.
+ @param end: a L{DateTime} specifying the end of the given time span.
C{end} may be None, indicating that there is no end date.
@param defaulttz: the default L{PyTimezone} to use in datetime comparisons.
@return: True if the property's date/date-time value is within the given time range,
@@ -337,7 +357,7 @@
# get date/date-time value
dt = self._pycalendar.getValue().getValue()
- assert isinstance(dt, PyCalendarDateTime), "Not a date/date-time value: %r" % (self,)
+ assert isinstance(dt, DateTime), "Not a date/date-time value: %r" % (self,)
return timeRangesOverlap(dt, None, start, end, defaulttz)
@@ -375,66 +395,95 @@
# Hidden instance.
HIDDEN_INSTANCE_PROPERTY = "X-CALENDARSERVER-HIDDEN-INSTANCE"
+ allowedTypesList = None
+
+
@classmethod
- def allFromString(clazz, string):
+ def allowedTypes(cls):
+ if cls.allowedTypesList is None:
+ cls.allowedTypesList = ["text/calendar"]
+ if config.EnableJSONData:
+ cls.allowedTypesList.append("application/calendar+json")
+ return cls.allowedTypesList
+
+
+ @classmethod
+ def allFromString(clazz, string, format=None):
"""
Just default to reading a single VCALENDAR
"""
- return clazz.fromString(string)
+ return clazz.fromString(string, format)
@classmethod
- def allFromStream(clazz, stream):
+ def allFromStream(clazz, stream, format=None):
"""
Just default to reading a single VCALENDAR
"""
- return clazz.fromStream(stream)
+ return clazz.fromStream(stream, format)
@classmethod
- def fromString(clazz, string):
+ def fromString(clazz, string, format=None):
"""
Construct a L{Component} from a string.
@param string: a string containing iCalendar data.
@return: a L{Component} representing the first component described by
C{string}.
"""
- if type(string) is unicode:
- string = string.encode("utf-8")
- else:
- # Valid utf-8 please
- string.decode("utf-8")
+ return clazz._fromData(string, False, format)
- # No BOMs please
- if string[:3] == codecs.BOM_UTF8:
- string = string[3:]
- return clazz.fromStream(StringIO.StringIO(string))
+ @classmethod
+ def fromStream(clazz, stream, format=None):
+ """
+ Construct a L{Component} from a stream.
+ @param stream: a C{read()}able stream containing iCalendar data.
+ @return: a L{Component} representing the first component described by
+ C{stream}.
+ """
+ return clazz._fromData(stream, True, format)
@classmethod
- def fromStream(clazz, stream):
+ def _fromData(clazz, data, isstream, format=None):
"""
Construct a L{Component} from a stream.
@param stream: a C{read()}able stream containing iCalendar data.
+ @param format: a C{str} indicating whether the data is iCalendar or jCal
@return: a L{Component} representing the first component described by
C{stream}.
"""
- cal = PyCalendar()
+
+ if isstream:
+ pass
+ else:
+ if type(data) is unicode:
+ data = data.encode("utf-8")
+ else:
+ # Valid utf-8 please
+ data.decode("utf-8")
+
+ # No BOMs please
+ if data[:3] == codecs.BOM_UTF8:
+ data = data[3:]
+
errmsg = "Unknown"
try:
- result = cal.parse(stream)
- except PyCalendarError, e:
+ result = Calendar.parseData(data, format)
+ except ErrorBase, e:
errmsg = "%s: %s" % (e.mReason, e.mData,)
result = None
if not result:
- stream.seek(0)
- raise InvalidICalendarDataError("%s\n%s" % (errmsg, stream.read(),))
- return clazz(None, pycalendar=cal)
+ if isstream:
+ data.seek(0)
+ data = data.read()
+ raise InvalidICalendarDataError("%s\n%s" % (errmsg, data,))
+ return clazz(None, pycalendar=result)
@classmethod
- def fromIStream(clazz, stream):
+ def fromIStream(clazz, stream, format=None):
"""
Construct a L{Component} from a stream.
@param stream: an L{IStream} containing iCalendar data.
@@ -448,7 +497,7 @@
# request stream.
#
def parse(data):
- return clazz.fromString(data)
+ return clazz.fromString(data, format)
return allDataFromStream(IStream(stream), parse)
@@ -480,8 +529,8 @@
pyobj = kwargs["pycalendar"]
if pyobj is not None:
- if not isinstance(pyobj, PyCalendarComponentBase):
- raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))
+ if not isinstance(pyobj, ComponentBase):
+ raise TypeError("Not a ComponentBase: %r" % (pyobj,))
self._pycalendar = pyobj
else:
@@ -499,7 +548,7 @@
self._parent = None
else:
# FIXME: figure out creating an arbitrary component
- self._pycalendar = PyCalendar(add_defaults=False) if name == "VCALENDAR" else PyCalendar.makeComponent(name, None)
+ self._pycalendar = Calendar(add_defaults=False) if name == "VCALENDAR" else PyComponent.makeComponent(name, None)
self._parent = None
@@ -542,13 +591,20 @@
return self._pycalendar == other._pycalendar
- def getTextWithTimezones(self, includeTimezones):
+ def getText(self, format=None):
+ return self.getTextWithTimezones(False, format)
+
+
+ def getTextWithTimezones(self, includeTimezones, format=None):
"""
- Return text representation and include timezones if the option is on
+ Return text representation and include timezones if the option is on.
"""
assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
- return self._pycalendar.getText(includeTimezones=includeTimezones)
+ result = self._pycalendar.getText(includeTimezones=includeTimezones, format=format)
+ if result is None:
+ raise ValueError("Unknown format requested for calendar data.")
+ return result
# FIXME: Should this not be in __eq__?
@@ -627,14 +683,14 @@
This also returns the matching master component if recurrence_id is C{None}.
@param recurrence_id: The RECURRENCE-ID property value to match.
- @type recurrence_id: L{PyCalendarDateTime}
+ @type recurrence_id: L{DateTime}
@return: the L{Component} for the overridden component,
or C{None} if there isn't one.
"""
assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
if isinstance(recurrence_id, str):
- recurrence_id = PyCalendarDateTime.parseText(recurrence_id) if recurrence_id else None
+ recurrence_id = DateTime.parseText(recurrence_id) if recurrence_id else None
for component in self.subcomponents():
if component.name() in ignoredComponents:
@@ -760,7 +816,7 @@
Return the start date or date-time for the specified component
converted to UTC.
@param component: the Component whose start should be returned.
- @return: the L{PyCalendarDateTime} for the start.
+ @return: the L{DateTime} for the start.
"""
dtstart = self.propertyValue("DTSTART")
return dtstart.duplicateAsUTC() if dtstart is not None else None
@@ -772,7 +828,7 @@
taking into account the presence or absence of DTEND/DURATION properties.
The returned date-time is converted to UTC.
@param component: the Component whose end should be returned.
- @return: the L{PyCalendarDateTime} for the end.
+ @return: the L{DateTime} for the end.
"""
dtend = self.propertyValue("DTEND")
if dtend is None:
@@ -789,7 +845,7 @@
Return the due date or date-time for the specified component
converted to UTC. Use DTSTART/DURATION if no DUE property.
@param component: the Component whose start should be returned.
- @return: the L{PyCalendarDateTime} for the start.
+ @return: the L{DateTime} for the start.
"""
due = self.propertyValue("DUE")
if due is None:
@@ -827,7 +883,7 @@
"""
Return the recurrence-id for the specified component.
@param component: the Component whose r-id should be returned.
- @return: the L{PyCalendarDateTime} for the r-id.
+ @return: the L{DateTime} for the r-id.
"""
rid = self.propertyValue("RECURRENCE-ID")
return rid.duplicateAsUTC() if rid is not None else None
@@ -1086,7 +1142,7 @@
to match the new limit, remove RDATEs/EXDATEs and overridden components beyond the limit.
@param rid: the recurrence-id limit
- @type rid: L{PyCalendarDateTime}
+ @type rid: L{DateTime}
"""
if not self.isRecurring():
@@ -1148,7 +1204,7 @@
match any RRULE pattern.
@param rid: the recurrence-id limit
- @type rid: L{PyCalendarDateTime}
+ @type rid: L{DateTime}
"""
if not self.isRecurring():
@@ -1169,7 +1225,7 @@
rrule_expanded = []
rrule.expand(
master.propertyValue("DTSTART"),
- PyCalendarPeriod(PyCalendarDateTime(1900, 1, 1), upperlimit),
+ Period(DateTime(1900, 1, 1), upperlimit),
rrule_expanded,
)
for i in sorted(rrule_expanded):
@@ -1244,14 +1300,14 @@
instance in the specified range. Date-times are converted to UTC. A
new calendar object is returned.
- @param start: the L{PyCalendarDateTime} for the start of the range.
- @param end: the L{PyCalendarDateTime} for the end of the range.
- @param timezone: the L{Component} or L{PyCalendarTimezone} of the VTIMEZONE to use for floating/all-day.
+ @param start: the L{DateTime} for the start of the range.
+ @param end: the L{DateTime} for the end of the range.
+ @param timezone: the L{Component} or L{Timezone} of the VTIMEZONE to use for floating/all-day.
@return: the L{Component} for the new calendar with expanded instances.
"""
if timezone is not None and isinstance(timezone, Component):
- pytz = PyCalendarTimezone(tzid=timezone.propertyValue("TZID"))
+ pytz = Timezone(tzid=timezone.propertyValue("TZID"))
else:
pytz = timezone
@@ -1297,7 +1353,7 @@
# Convert all datetime properties to UTC unless they are floating
for property in newcomp.properties():
value = property.value()
- if isinstance(value, PyCalendarDateTime) and value.local():
+ if isinstance(value, DateTime) and value.local():
property.removeParameter("TZID")
property.setValue(value.duplicateAsUTC())
@@ -1324,7 +1380,7 @@
breathing room to return results for future instances.
@param limit: the max datetime to cache up to.
- @type limit: L{PyCalendarDateTime}
+ @type limit: L{DateTime}
"""
# Checked for cached values first
@@ -1335,7 +1391,7 @@
# so return cached instances
return self.cachedInstances
- lookAheadLimit = limit + PyCalendarDuration(days=365)
+ lookAheadLimit = limit + Duration(days=365)
self.cachedInstances = self.expandTimeRanges(
lookAheadLimit,
ignoreInvalidInstances=ignoreInvalidInstances
@@ -1349,7 +1405,7 @@
contained within this VCALENDAR component. We will assume
that this component has already been validated as a CalDAV resource
(i.e. only one type of component, all with the same UID)
- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
+ @param limit: L{DateTime} value representing the end of the expansion.
@param ignoreInvalidInstances: C{bool} whether to ignore instance errors.
@return: a set of Instances for each recurrence in the set.
"""
@@ -1368,14 +1424,14 @@
@param componentSet: the set of components that are to make up the
recurrence set. These MUST all be components with the same UID
and type, forming a proper recurring set.
- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
+ @param limit: L{DateTime} value representing the end of the expansion.
@param componentSet: the set of components that are to make up the recurrence set.
These MUST all be components with the same UID and type, forming a proper
recurring set.
@type componentSet: C{list}
@param limit: the end of the expansion
- @type limit: L{PyCalendarDateTime}
+ @type limit: L{DateTime}
@param ignoreInvalidInstances: whether or not invalid recurrences raise an exception
@type ignoreInvalidInstances: C{bool}
@param normalizeFunction: a function used to normalize date/time values in instances
@@ -1451,7 +1507,7 @@
There is always some new thing that will surprise you.
@param rid: recurrence-id value
- @type rid: L{PyCalendarDateTime} or C{str}
+ @type rid: L{DateTime} or C{str}
@param allowCancelled: whether to allow a STATUS:CANCELLED override
@type allowCancelled: C{bool}
@param allowExcluded: whether to derive an instance for an existing EXDATE
@@ -1469,7 +1525,7 @@
return None
if isinstance(rid, str):
- rid = PyCalendarDateTime.parseText(rid) if rid else None
+ rid = DateTime.parseText(rid) if rid else None
# TODO: Check that the recurrence-id is a valid instance
# For now we just check that there is no matching EXDATE
@@ -1586,7 +1642,7 @@
# Pre-cache instance expansion up to the highest rid
highest_rid = max(non_master_rids)
self.cacheExpandedTimeRanges(
- highest_rid + PyCalendarDuration(days=1),
+ highest_rid + Duration(days=1),
ignoreInvalidInstances=ignoreInvalidInstances
)
for rid in rids:
@@ -1600,7 +1656,7 @@
Test whether the specified recurrence-id is a valid instance in this event.
@param rid: recurrence-id value
- @type rid: L{PyCalendarDateTime}
+ @type rid: L{DateTime}
@return: C{bool}
"""
@@ -1984,12 +2040,12 @@
def gettimezone(self):
"""
- Get the PyCalendarTimezone for a Timezone component.
+ Get the Timezone for a Timezone component.
- @return: L{PyCalendarTimezone} if this is a VTIMEZONE, otherwise None.
+ @return: L{Timezone} if this is a VTIMEZONE, otherwise None.
"""
if self.name() == "VTIMEZONE":
- return PyCalendarTimezone(tzid=self._pycalendar.getID())
+ return Timezone(tzid=self._pycalendar.getID())
elif self.name() == "VCALENDAR":
for component in self.subcomponents():
if component.name() == "VTIMEZONE":
@@ -2137,6 +2193,10 @@
return is_server
+ def recipientPropertyName(self):
+ return "VOTER" if self.name() == "VPOLL" else "ATTENDEE"
+
+
def getAttendees(self):
"""
Get the attendee value. Works on either a VCALENDAR or on a component.
@@ -2152,7 +2212,7 @@
return component.getAttendees()
else:
# Find the property values
- return [p.value() for p in self.properties("ATTENDEE")]
+ return [p.value() for p in self.properties(self.recipientPropertyName())]
return None
@@ -2179,7 +2239,7 @@
result = ()
attendees = set()
rid = self.getRecurrenceIDUTC()
- for attendee in tuple(self.properties("ATTENDEE")):
+ for attendee in tuple(self.properties(self.recipientPropertyName())):
if onlyScheduleAgentServer:
if attendee.hasParameter("SCHEDULE-AGENT"):
@@ -2195,6 +2255,27 @@
return result
+ def getVoterProperty(self, match):
+ """
+ Get the voters matching a value.
+
+ @param match: a C{list} of calendar user address strings to try and match.
+ @return: the matching Voter property, or None
+ """
+
+ # Need to normalize http/https cu addresses
+ test = set()
+ for item in match:
+ test.add(normalizeCUAddr(item))
+
+ # Find the primary subcomponent
+ for voter in self.properties("VOTER"):
+ if normalizeCUAddr(voter.value()) in test:
+ return voter
+
+ return None
+
+
def getAttendeeProperty(self, match):
"""
Get the attendees matching a value. Works on either a VCALENDAR or on a component.
@@ -2217,7 +2298,7 @@
return attendee
else:
# Find the primary subcomponent
- for attendee in self.properties("ATTENDEE"):
+ for attendee in self.properties(self.recipientPropertyName()):
if normalizeCUAddr(attendee.value()) in test:
return attendee
@@ -2260,7 +2341,7 @@
yield attendee
else:
# Find the primary subcomponent
- for attendee in self.properties("ATTENDEE"):
+ for attendee in self.properties(self.recipientPropertyName()):
yield attendee
@@ -2641,7 +2722,7 @@
for component in self.subcomponents():
if component.name() in ignoredComponents:
continue
- [component.removeProperty(p) for p in tuple(component.properties("ATTENDEE")) if p.value().lower() != attendee.lower()]
+ [component.removeProperty(p) for p in tuple(component.properties(component.recipientPropertyName())) if p.value().lower() != attendee.lower()]
def removeAllButTheseAttendees(self, attendees):
@@ -2656,7 +2737,7 @@
for component in self.subcomponents():
if component.name() in ignoredComponents:
continue
- [component.removeProperty(p) for p in tuple(component.properties("ATTENDEE")) if p.value().lower() not in attendees]
+ [component.removeProperty(p) for p in tuple(component.properties(component.recipientPropertyName())) if p.value().lower() not in attendees]
def hasAlarm(self):
@@ -2977,7 +3058,7 @@
# Bump all components
self.replacePropertyInAllComponents(Property("SEQUENCE", newseq))
- self.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ self.replacePropertyInAllComponents(Property("DTSTAMP", DateTime.getNowUTC()))
def sequenceInSync(self, oldcalendar):
@@ -3059,7 +3140,7 @@
dtend = self.getProperty("DTEND")
duration = self.getProperty("DURATION")
- timeRange = PyCalendarPeriod(
+ timeRange = Period(
start=dtstart.value(),
end=dtend.value() if dtend is not None else None,
duration=duration.value() if duration is not None else None,
@@ -3163,7 +3244,8 @@
continue
for prop in itertools.chain(
component.properties("ORGANIZER"),
- component.properties("ATTENDEE")
+ component.properties("ATTENDEE"),
+ component.properties("VOTER")
):
# Check that we can lookup this calendar user address - if not
@@ -3261,7 +3343,11 @@
else:
prop.removeParameter("EMAIL")
+ # For VPOLL also do immediate children
+ if component.name() == "VPOLL":
+ component.normalizeCalendarUserAddresses(lookupFunction, principalFunction, toUUID)
+
def allPerUserUIDs(self):
results = set()
@@ -3311,7 +3397,7 @@
Determine whether an event exists completely prior to a given moment.
@param limit: the moment to compare against.
- @type limit: L{PyCalendarDateTime}
+ @type limit: L{DateTime}
@return: a C{bool}, True if the event has any instances occurring after
limit, False otherwise.
@@ -3369,13 +3455,13 @@
start.setDateOnly(False)
if tzexpanded:
if start != tzexpanded[0][0]:
- results.append((str(start), PyCalendarUTCOffsetValue(tzexpanded[0][1]).getText(),))
+ results.append((str(start), UTCOffsetValue(tzexpanded[0][1]).getText(),))
else:
- results.append((str(start), PyCalendarUTCOffsetValue(tzcomp._pycalendar.getTimezoneOffsetSeconds(start)).getText(),))
+ results.append((str(start), UTCOffsetValue(tzcomp._pycalendar.getTimezoneOffsetSeconds(start)).getText(),))
for tzstart, _ignore_tzoffsetfrom, tzoffsetto in tzexpanded:
results.append((
tzstart.getText(),
- PyCalendarUTCOffsetValue(tzoffsetto).getText(),
+ UTCOffsetValue(tzoffsetto).getText(),
))
return results
@@ -3388,7 +3474,7 @@
time range.
@param tzdata: the iCalendar data containing a VTIMEZONE.
- @type tzdata: L{PyCalendar}
+ @type tzdata: L{Calendar}
@param start: date for the start of the expansion.
@type start: C{date}
@param end: date for the end of the expansion.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -29,14 +29,14 @@
"""
CalDAV resource.
"""
- def isCalendarCollection():
+ def isCalendarCollection(): #@NoSelf
"""
(CalDAV-access-10, Section 4.2)
@return: True if this resource is a calendar collection, False
otherwise.
"""
- def isSpecialCollection(collectiontype):
+ def isSpecialCollection(collectiontype): #@NoSelf
"""
(CalDAV-access-10, Section 4.2)
@param collectiontype: L{WebDAVElement} for the collection type to test for.
@@ -44,30 +44,30 @@
False otherwise.
"""
- def isPseudoCalendarCollection():
+ def isPseudoCalendarCollection(): #@NoSelf
"""
@return: True if this resource is a calendar collection like (e.g.
a regular calendar collection or schedule inbox/outbox), False
otherwise.
"""
- def findCalendarCollections(depth):
+ def findCalendarCollections(depth): #@NoSelf
"""
Returns an iterable of child calendar collection resources for the given
depth.
- Because resources do not know their request URIs, chidren are returned
+ Because resources do not know their request URIs, children are returned
as tuples C{(resource, uri)}, where C{resource} is the child resource
and C{uri} is a URL path relative to this resource.
@param depth: the search depth (one of "0", "1", or "infinity")
@return: an iterable of tuples C{(resource, uri)}.
"""
- def createCalendar(request):
+ def createCalendar(request): #@NoSelf
"""
Create a calendar collection for this resource.
"""
- def iCalendar():
+ def iCalendar(): #@NoSelf
"""
Instantiate an iCalendar component object representing this resource or
its child with the given name.
@@ -78,52 +78,42 @@
@return: a L{twistedcaldav.ical.Component} of type C{"VCALENDAR"}.
"""
- def iCalendarText():
- """
- Obtains the iCalendar text representing this resource or its child with
- the given name.
- The behavior of this method is not specified if it is called on a
- resource that is not a calendar collection or a calendar resource within
- a calendar collection.
- @return: a string containing iCalendar text with a top-level component
- of type C{"VCALENDAR"}.
- """
class ICalendarPrincipalResource(IDAVResource):
"""
CalDAV principle resource.
"""
- def principalUID():
+ def principalUID(): #@NoSelf
"""
@return: the user id for this principal.
"""
- def calendarHomeURLs():
+ def calendarHomeURLs(): #@NoSelf
"""
@return: a list of calendar home URLs for this principal's calendar user.
"""
- def calendarUserAddresses():
+ def calendarUserAddresses(): #@NoSelf
"""
@return: a list of calendar user addresses for this principal's calendar
user.
"""
- def calendarFreeBusyURIs(self, request):
+ def calendarFreeBusyURIs(request): #@NoSelf
"""
@param request: the request being processed.
@return: a L{Deferred} list of URIs for calendars that contribute to
free-busy for this principal's calendar user.
"""
- def scheduleInboxURL():
+ def scheduleInboxURL(): #@NoSelf
"""
Get the schedule INBOX URL for this principal's calendar user.
@return: a string containing the URL from the schedule-inbox-URL property.
"""
- def scheduleOutboxURL():
+ def scheduleOutboxURL(): #@NoSelf
"""
Get the schedule OUTBOX URL for this principal's calendar user.
@return: a string containing the URL from the schedule-outbox-URL property.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,10 +21,10 @@
from twistedcaldav.config import config
from twistedcaldav.dateops import normalizeForIndex, differenceDateTime
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
class TooManyInstancesError(Exception):
@@ -80,7 +80,7 @@
(trigger, related, repeat, duration) = alarm.getTriggerDetails()
# Handle relative vs absolute triggers
- if isinstance(trigger, PyCalendarDateTime):
+ if isinstance(trigger, DateTime):
# Absolute trigger
start = trigger
else:
@@ -135,7 +135,7 @@
@param componentSet: the set of components that are to make up the
recurrence set. These MUST all be components with the same UID
and type, forming a proper recurring set.
- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
+ @param limit: L{DateTime} value representing the end of the expansion.
"""
# Look at each component type
@@ -232,10 +232,10 @@
if end is None:
if not start.isDateOnly():
# Timed event with zero duration
- duration = PyCalendarDuration(days=0)
+ duration = Duration(days=0)
else:
# All day event default duration is one day
- duration = PyCalendarDuration(days=1)
+ duration = Duration(days=1)
end = start + duration
else:
duration = differenceDateTime(start, end)
@@ -248,7 +248,7 @@
Add the specified master VEVENT Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end L{PyCalendarDateTime} for expansion
+ @param limit: the end L{DateTime} for expansion
"""
details = self._getMasterEventDetails(component)
@@ -330,7 +330,7 @@
Add the specified master VTODO Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end L{PyCalendarDateTime} for expansion
+ @param limit: the end L{DateTime} for expansion
"""
details = self._getMasterToDoDetails(component)
if details is None:
@@ -370,7 +370,7 @@
# than the master DTSTART, and if we exclude those, the associated
# overridden instances will cause an InvalidOverriddenInstance.
limited = rrules.expand(rulestart,
- PyCalendarPeriod(PyCalendarDateTime(1900, 1, 1), upperlimit), expanded)
+ Period(DateTime(1900, 1, 1), upperlimit), expanded)
for startDate in expanded:
startDate = self.normalizeFunction(startDate)
endDate = startDate + duration
@@ -478,7 +478,7 @@
Add the specified master VFREEBUSY Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end L{PyCalendarDateTime} for expansion
+ @param limit: the end L{DateTime} for expansion
"""
start = component.getStartDateUTC()
@@ -519,7 +519,7 @@
depending on the presence of the properties. If unbounded at one or both ends, we will
set the time to 1/1/1900 in the past and 1/1/3000 in the future.
@param component: the Component to expand
- @param limit: the end L{PyCalendarDateTime} for expansion
+ @param limit: the end L{DateTime} for expansion
"""
start = component.getStartDateUTC()
@@ -531,7 +531,7 @@
# If the availability is beyond the end of the range we want, ignore it
return
if start is None:
- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
start = self.normalizeFunction(start)
end = component.getEndDateUTC()
@@ -539,7 +539,7 @@
# If the availability is before the start of the range we want, ignore it
return
if end is None:
- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
end = self.normalizeFunction(end)
self.addInstance(Instance(component, start, end))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -47,16 +47,17 @@
case of a missing underlying resource (broken link) as indicated by self._linkedResource being None.
"""
log = Logger()
-
+
def __init__(self, parent, link_url):
self.parent = parent
self.linkURL = link_url
self.loopDetect = set()
super(LinkResource, self).__init__(self.parent.principalCollections())
+
@inlineCallbacks
def linkedResource(self, request):
-
+
if not hasattr(self, "_linkedResource"):
if self.linkURL in self.loopDetect:
raise HTTPError(StatusResponse(responsecode.LOOP_DETECTED, "Recursive link target: %s" % (self.linkURL,)))
@@ -67,17 +68,20 @@
if self._linkedResource is None:
raise HTTPError(StatusResponse(responsecode.NOT_FOUND, "Missing link target: %s" % (self.linkURL,)))
-
+
returnValue(self._linkedResource)
+
def isCollection(self):
return True if hasattr(self, "_linkedResource") else False
+
def resourceType(self):
return self._linkedResource.resourceType() if hasattr(self, "_linkedResource") else davxml.ResourceType.link
-
+
+
def locateChild(self, request, segments):
-
+
def _defer(result):
if result is None:
return (self, server.StopTraversal)
@@ -87,6 +91,7 @@
d.addCallback(_defer)
return d
+
@inlineCallbacks
def renderHTTP(self, request):
linked_to = (yield self.linkedResource(request))
@@ -95,27 +100,33 @@
else:
returnValue(http.StatusResponse(responsecode.OK, "Link resource with missing target: %s" % (self.linkURL,)))
+
def getChild(self, name):
return self._linkedResource.getChild(name) if hasattr(self, "_linkedResource") else None
+
@inlineCallbacks
def hasProperty(self, property, request):
hosted = (yield self.linkedResource(request))
result = (yield hosted.hasProperty(property, request)) if hosted else False
returnValue(result)
+
@inlineCallbacks
def readProperty(self, property, request):
hosted = (yield self.linkedResource(request))
result = (yield hosted.readProperty(property, request)) if hosted else None
returnValue(result)
+
@inlineCallbacks
def writeProperty(self, property, request):
hosted = (yield self.linkedResource(request))
result = (yield hosted.writeProperty(property, request)) if hosted else None
returnValue(result)
+
+
class LinkFollowerMixIn(object):
@inlineCallbacks
@@ -128,6 +139,5 @@
if linked_to is None:
break
resource = linked_to
-
+
returnValue((resource, path))
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,7 +24,7 @@
from locale import normalize
from twext.python.log import Logger
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.duration import Duration
try:
from Foundation import (
@@ -89,12 +89,12 @@
helper methods for date formatting:
with translationTo('en') as trans:
- print(trans.dtDate(PyCalendarDateTime.getToday()))
+ print(trans.dtDate(DateTime.getToday()))
... Thursday, October 23, 2008
with translationTo('fr') as trans:
- print(trans.dtDate(PyCalendarDateTime.getToday()))
+ print(trans.dtDate(DateTime.getToday()))
... Jeudi, Octobre 23, 2008
@@ -124,12 +124,13 @@
localedir=localeDir, languages=[lang, 'en'], fallback=True)
self.translations[key] = self.translation
+
def __enter__(self):
# Get the caller's globals so we can rebind their '_' to our translator
caller_globals = inspect.stack()[1][0].f_globals
# Store whatever '_' is already bound to so we can restore it later
- if caller_globals.has_key('_'):
+ if '_' in caller_globals:
self.prev = caller_globals['_']
# Rebind '_' to our translator
@@ -138,6 +139,7 @@
# What we return here is accessible to the caller via the 'as' clause
return self
+
def __exit__(self, type, value, traceback):
# Restore '_' if it previously had a value
if hasattr(self, 'prev'):
@@ -146,13 +148,16 @@
# Don't swallow exceptions
return False
+
def monthAbbreviation(self, monthNumber):
return self.translation.ugettext(monthsAbbrev[monthNumber])
+
def date(self, component):
dtStart = component.propertyValue("DTSTART")
return self.dtDate(dtStart)
+
def time(self, component):
"""
Examples:
@@ -191,9 +196,9 @@
else:
if dtStart.isDateOnly():
dtEnd = None
- duration = PyCalendarDuration(days=1)
+ duration = Duration(days=1)
else:
- dtEnd = dtStart + PyCalendarDuration(days=1)
+ dtEnd = dtStart + Duration(days=1)
dtEnd.setHHMMSS(0, 0, 0)
duration = dtEnd - dtStart
@@ -225,6 +230,7 @@
}
)
+
def dtTime(self, val, includeTimezone=True):
if val.isDateOnly():
return ""
@@ -252,6 +258,7 @@
return result
+
def dtDuration(self, val):
# Bind to '_' so pygettext.py will pick this up for translation
@@ -265,7 +272,7 @@
parts.append(_("1 day"))
elif days > 1:
parts.append(_("%(dayCount)d days") %
- { 'dayCount' : days })
+ {'dayCount' : days})
hours = divmod(total / 3600, 24)[1]
minutes = divmod(total / 60, 60)[1]
@@ -275,19 +282,19 @@
parts.append(_("1 hour"))
elif hours > 1:
parts.append(_("%(hourCount)d hours") %
- { 'hourCount' : hours })
+ {'hourCount' : hours})
if minutes == 1:
parts.append(_("1 minute"))
elif minutes > 1:
parts.append(_("%(minuteCount)d minutes") %
- { 'minuteCount' : minutes })
+ {'minuteCount' : minutes})
if seconds == 1:
parts.append(_("1 second"))
elif seconds > 1:
parts.append(_("%(secondCount)d seconds") %
- { 'secondCount' : seconds })
+ {'secondCount' : seconds})
return " ".join(parts)
@@ -398,12 +405,14 @@
else:
log.info("%s is up to date" % (moFile,))
+
+
class ParseError(Exception):
pass
+
def convertStringsFile(src, dest):
- strings = { }
dir = os.path.dirname(dest)
@@ -458,13 +467,13 @@
result = struct.pack(
"Iiiiiii",
- 0x950412DEL, # magic number
- 0, # file format revision
- len(originals), # number of strings
- 28, # offset of table with original strings
- 28+len(originals)*8, # offset of table with translation strings
- 0, # size of hashing table
- 0 # offset of hashing table
+ 0x950412DEL, # magic number
+ 0, # file format revision
+ len(originals), # number of strings
+ 28, # offset of table with original strings
+ 28 + len(originals) * 8, # offset of table with translation strings
+ 0, # size of hashing table
+ 0 # offset of hashing table
)
result += array.array("i", keyDescriptors).tostring()
result += array.array("i", valueDescriptors).tostring()
@@ -475,6 +484,7 @@
outFile.write(result)
+
def getLanguage(config):
"""
If the language has been specified explicitly in the config, return it. Otherwise
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,7 +15,7 @@
##
from twistedcaldav.memcacher import Memcacher
-from twisted.internet.defer import inlineCallbacks, Deferred, returnValue,\
+from twisted.internet.defer import inlineCallbacks, Deferred, returnValue, \
succeed
from twisted.internet import reactor
import time
@@ -24,7 +24,7 @@
def __init__(self, namespace, locktoken, timeout=5.0, retry_interval=0.1, expire_time=0):
"""
-
+
@param namespace: a unique namespace for this lock's tokens
@type namespace: C{str}
@param locktoken: the name of the locktoken
@@ -44,31 +44,33 @@
self._expire_time = expire_time
self._hasLock = False
+
def _getMemcacheProtocol(self):
-
+
result = super(MemcacheLock, self)._getMemcacheProtocol()
if isinstance(result, Memcacher.nullCacher):
raise AssertionError("No implementation of shared locking without memcached")
-
+
return result
+
@inlineCallbacks
def acquire(self):
-
+
assert not self._hasLock, "Lock already acquired."
-
+
timeout_at = time.time() + self._timeout
waiting = False
while True:
-
+
result = (yield self.add(self._locktoken, "1", expireTime=self._expire_time))
if result:
self._hasLock = True
if waiting:
self.log.debug("Got lock after waiting on %s" % (self._locktoken,))
break
-
+
if self._timeout and time.time() < timeout_at:
waiting = True
self.log.debug("Waiting for lock on %s" % (self._locktoken,))
@@ -80,13 +82,14 @@
else:
self.log.debug("Timed out lock after waiting on %s" % (self._locktoken,))
raise MemcacheLockTimeoutError()
-
+
returnValue(True)
+
def release(self):
-
+
assert self._hasLock, "Lock not acquired."
-
+
def _done(result):
self._hasLock = False
return result
@@ -95,18 +98,20 @@
d.addCallback(_done)
return d
+
def clean(self):
-
+
if self._hasLock:
return self.release()
else:
return succeed(True)
+
def locked(self):
"""
Test if the lock is currently being held.
"""
-
+
def _gotit(value):
return value is not None
@@ -114,5 +119,7 @@
d.addCallback(_gotit)
return d
+
+
class MemcacheLockTimeoutError(Exception):
pass
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -99,6 +99,7 @@
connector,
reason)
+
def buildProtocol(self, addr):
"""
Attach the C{self.connectionPool} to the protocol so it can tell it,
@@ -167,6 +168,7 @@
self._pendingConnects = 0
self._commands = []
+
def _isIdle(self):
return (
len(self._busyClients) == 0 and
@@ -174,6 +176,7 @@
self._pendingConnects == 0
)
+
def _shutdownCallback(self):
self.shutdown_requested = True
if self._isIdle():
@@ -181,13 +184,14 @@
self.shutdown_deferred = Deferred()
return self.shutdown_deferred
+
def _newClientConnection(self):
"""
Create a new client connection.
@return: A L{Deferred} that fires with the L{IProtocol} instance.
"""
- self.log.debug("Initating new client connection to: %r" % (
+ self.log.debug("Initiating new client connection to: %r" % (
self._endpoint,))
self._logClientStats()
@@ -219,7 +223,7 @@
@param command: A C{str} representing an attribute of
L{MemCacheProtocol}.
- @parma args: Any positional arguments that should be passed to
+ @param args: Any positional arguments that should be passed to
C{command}.
@param kwargs: Any keyword arguments that should be passed to
C{command}.
@@ -258,7 +262,7 @@
@param command: A C{str} representing an attribute of
L{MemCacheProtocol}.
- @parma args: Any positional arguments that should be passed to
+ @param args: Any positional arguments that should be passed to
C{command}.
@param kwargs: Any keyword arguments that should be passed to
C{command}.
@@ -371,24 +375,31 @@
def get(self, *args, **kwargs):
return self.performRequest('get', *args, **kwargs)
+
def set(self, *args, **kwargs):
return self.performRequest('set', *args, **kwargs)
+
def checkAndSet(self, *args, **kwargs):
return self.performRequest('checkAndSet', *args, **kwargs)
+
def delete(self, *args, **kwargs):
return self.performRequest('delete', *args, **kwargs)
+
def add(self, *args, **kwargs):
return self.performRequest('add', *args, **kwargs)
+
def incr(self, *args, **kwargs):
return self.performRequest('increment', *args, **kwargs)
+
def decr(self, *args, **kwargs):
return self.performRequest('decrement', *args, **kwargs)
+
def flushAll(self, *args, **kwargs):
return self.performRequest('flushAll', *args, **kwargs)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -25,7 +25,7 @@
from txdav.xml import element as davxml
from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.util import parentForURL
-from twext.web2.http import HTTPError
+from twext.web2.http import HTTPError, StatusResponse
from twext.web2.http import Response
from twext.web2.http_headers import MimeType
from twext.web2.stream import MemoryStream
@@ -34,8 +34,10 @@
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.datafilters.hiddeninstance import HiddenInstanceFilter
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
+from twistedcaldav.ical import Component
from twistedcaldav.resource import isPseudoCalendarCollectionResource, \
CalDAVResource
+from twistedcaldav.util import bestAcceptType
@inlineCallbacks
def http_GET(self, request):
@@ -70,6 +72,8 @@
returnValue(response)
else:
+ # FIXME: this should be implemented in storebridge.CalendarObject.render
+
# Look for calendar access restriction on existing resource.
parentURL = parentForURL(request.uri)
parent = (yield request.locateResource(parentURL))
@@ -78,6 +82,11 @@
# Check authorization first
yield self.authorize(request, (davxml.Read(),))
+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
caldata = (yield self.iCalendarForUser(request))
# Filter any attendee hidden instances
@@ -92,8 +101,8 @@
caldata = PrivateEventFilter(self.accessMode, isowner).filter(caldata)
response = Response()
- response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
+ response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
# Add Schedule-Tag header if property is present
if self.scheduleTag:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -98,7 +98,7 @@
search_properties = "names"
elif isinstance(container, davxml.PropertyContainer):
properties = container.children
- search_properties = [(p.namespace, p.name) for p in properties]
+ search_properties = properties
else:
raise AssertionError("Unexpected element type in %s: %s"
% (davxml.PropertyFind.sname(), container))
@@ -245,7 +245,11 @@
# Utilities
##
-def propertyName(name):
+def propertyName(prop):
+ if type(prop) is tuple:
+ name = prop
+ else:
+ name = prop.qname()
property_namespace, property_name = name
pname = davxml.WebDAVUnknownElement()
pname.namespace = property_namespace
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -32,7 +32,8 @@
from twext.web2.http import HTTPError, StatusResponse
from twistedcaldav import caldavxml
-from twistedcaldav.caldavxml import caldav_namespace, MaxInstances
+from twistedcaldav.caldavxml import caldav_namespace, MaxInstances, \
+ CalendarTimeZone
from twistedcaldav.config import config
from txdav.common.icommondatastore import IndexedSearchException, \
ConcurrentModification
@@ -171,10 +172,10 @@
if calresource.isPseudoCalendarCollection():
# Get the timezone property from the collection if one was not set in the query,
# and store in the query filter for later use
- has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
+ has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
timezone = query_timezone
if query_tz is None and has_prop:
- tz = (yield calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
+ tz = (yield calresource.readProperty(CalendarTimeZone(), request))
filter.settimezone(tz)
timezone = tuple(tz.calendar().subcomponents())[0]
@@ -233,9 +234,9 @@
parent = (yield calresource.locateParent(request, uri))
assert parent is not None and parent.isPseudoCalendarCollection()
- has_prop = (yield parent.hasProperty((caldav_namespace, "calendar-timezone"), request))
+ has_prop = (yield parent.hasProperty(CalendarTimeZone(), request))
if has_prop:
- tz = (yield parent.readProperty((caldav_namespace, "calendar-timezone"), request))
+ tz = (yield parent.readProperty(CalendarTimeZone(), request))
filter.settimezone(tz)
timezone = tuple(tz.calendar().subcomponents())[0]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -52,7 +52,7 @@
from twistedcaldav import caldavxml
from twistedcaldav import carddavxml
-from twistedcaldav.caldavxml import caldav_namespace, CalendarData, TimeRange
+from twistedcaldav.caldavxml import CalendarData, CalendarTimeZone, TimeRange
from twistedcaldav.carddavxml import AddressData
from twistedcaldav.config import config
from twistedcaldav.datafilters.calendardata import CalendarDataFilter
@@ -69,10 +69,10 @@
from txdav.common.icommondatastore import IndexedSearchException
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.period import PyCalendarPeriod
+from pycalendar.duration import Duration
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
+from pycalendar.period import Period
log = Logger()
@@ -299,7 +299,7 @@
generate_calendar_data = False
for property in prop.children:
if isinstance(property, caldavxml.CalendarData):
- if not property.verifyTypeVersion([("text/calendar", "2.0")]):
+ if not property.verifyTypeVersion():
result = False
message = "Calendar-data element type/version not supported: content-type: %s, version: %s" % (property.content_type, property.version)
generate_calendar_data = True
@@ -324,7 +324,7 @@
generate_address_data = False
for property in prop.children:
if isinstance(property, carddavxml.AddressData):
- if not property.verifyTypeVersion([("text/vcard", "3.0")]):
+ if not property.verifyTypeVersion():
result = False
message = "Address-data element type/version not supported: content-type: %s, version: %s" % (property.content_type, property.version)
generate_address_data = True
@@ -371,7 +371,7 @@
filtered = HiddenInstanceFilter().filter(calendar)
filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered)
filtered = CalendarDataFilter(property, timezone).filter(filtered)
- propvalue = CalendarData().fromCalendar(filtered)
+ propvalue = CalendarData.fromCalendar(filtered, format=property.content_type)
properties_by_status[responsecode.OK].append(propvalue)
continue
@@ -379,7 +379,7 @@
if vcard is None:
vcard = (yield resource.vCard())
filtered = AddressDataFilter(property).filter(vcard)
- propvalue = AddressData().fromAddress(filtered)
+ propvalue = AddressData.fromAddress(filtered, format=property.content_type)
properties_by_status[responsecode.OK].append(propvalue)
continue
@@ -392,7 +392,7 @@
if has:
try:
- prop = (yield resource.readProperty(qname, request))
+ prop = (yield resource.readProperty(property, request))
if prop is not None:
properties_by_status[responsecode.OK].append(prop)
elif not returnMinimal:
@@ -436,8 +436,8 @@
if entry:
# Offset one day at either end to account for floating
- cached_start = entry.timerange.start + PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
- cached_end = entry.timerange.end - PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_start = entry.timerange.start + Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_end = entry.timerange.end - Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
# Verify that the requested timerange lies within the cache timerange
if compareDateTime(timerange.end, cached_end) <= 0 and compareDateTime(timerange.start, cached_start) >= 0:
@@ -515,9 +515,9 @@
useruid = ""
# Get the timezone property from the collection.
- has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
+ has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
if has_prop:
- tz = (yield calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
+ tz = (yield calresource.readProperty(CalendarTimeZone(), request))
else:
tz = None
@@ -559,8 +559,8 @@
request.extendedLogItems["fb-uncached"] = request.extendedLogItems.get("fb-uncached", 0) + 1
# We want to cache a large range of time based on the current date
- cache_start = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=0 - config.FreeBusyCacheDaysBack))
- cache_end = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=config.FreeBusyCacheDaysForward))
+ cache_start = normalizeToUTC(DateTime.getToday() + Duration(days=0 - config.FreeBusyCacheDaysBack))
+ cache_end = normalizeToUTC(DateTime.getToday() + Duration(days=config.FreeBusyCacheDaysForward))
# If the requested timerange would fit in our allowed cache range, trigger the cache creation
if compareDateTime(timerange.start, cache_start) >= 0 and compareDateTime(timerange.end, cache_end) <= 0:
@@ -602,7 +602,7 @@
request.extendedLogItems["fb-cached"] = request.extendedLogItems.get("fb-cached", 0) + 1
# Determine appropriate timezone (UTC is the default)
- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
# We care about separate instances for VEVENTs only
aggregated_resources = {}
@@ -645,15 +645,15 @@
if float == 'Y':
fbstart.setTimezone(tzinfo)
else:
- fbstart.setTimezone(PyCalendarTimezone(utc=True))
+ fbstart.setTimezone(Timezone(utc=True))
fbend = parseSQLTimestampToPyCalendar(end)
if float == 'Y':
fbend.setTimezone(tzinfo)
else:
- fbend.setTimezone(PyCalendarTimezone(utc=True))
+ fbend.setTimezone(Timezone(utc=True))
# Clip instance to time range
- clipped = clipPeriod(PyCalendarPeriod(fbstart, duration=fbend - fbstart), PyCalendarPeriod(timerange.start, timerange.end))
+ clipped = clipPeriod(Period(fbstart, duration=fbend - fbstart), Period(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -735,7 +735,7 @@
@param timerange: the time-range in which to expand
@type timerange: L{TimeRange}
@param tzinfo: timezone for floating time calculations
- @type tzinfo: L{PyCalendarTimezone}
+ @type tzinfo: L{Timezone}
"""
# First expand the component
@@ -775,7 +775,7 @@
@param calendar: the L{Component} that is the VCALENDAR containing the VEVENT's.
@param fbinfo: the tuple used to store the three types of fb data.
@param timerange: the time range to restrict free busy data to.
- @param tzinfo: the L{PyCalendarTimezone} for the timezone to use for floating/all-day events.
+ @param tzinfo: the L{Timezone} for the timezone to use for floating/all-day events.
"""
# Expand out the set of instances for the event with in the required range
@@ -820,10 +820,10 @@
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = PyCalendarPeriod(fbstart, duration=fbend - fbstart)
+ period = Period(fbstart, duration=fbend - fbstart)
else:
- period = PyCalendarPeriod(fbstart, fbend)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ period = Period(fbstart, fbend)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -861,7 +861,7 @@
assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
for period in fb.value():
# Clip period for this instance
- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(timerange.start, timerange.end))
+ clipped = clipPeriod(period.getValue(), Period(timerange.start, timerange.end))
if clipped:
fbinfo[fbtype_mapper.get(fbtype, 0)].append(clipped)
@@ -880,12 +880,12 @@
# Get overall start/end
start = vav.getStartDateUTC()
if start is None:
- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
end = vav.getEndDateUTC()
if end is None:
- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- period = PyCalendarPeriod(start, end)
- overall = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ period = Period(start, end)
+ overall = clipPeriod(period, Period(timerange.start, timerange.end))
if overall is None:
continue
@@ -897,10 +897,10 @@
last_end = timerange.start
for period in periods:
if last_end < period.getStart():
- busyperiods.append(PyCalendarPeriod(last_end, period.getStart()))
+ busyperiods.append(Period(last_end, period.getStart()))
last_end = period.getEnd()
if last_end < timerange.end:
- busyperiods.append(PyCalendarPeriod(last_end, timerange.end))
+ busyperiods.append(Period(last_end, timerange.end))
# Add to actual results mapped by busy type
fbtype = vav.propertyValue("BUSYTYPE")
@@ -947,10 +947,10 @@
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = PyCalendarPeriod(start, duration=end - start)
+ period = Period(start, duration=end - start)
else:
- period = PyCalendarPeriod(start, end)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ period = Period(start, end)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
if clipped:
periods.append(clipped)
@@ -993,7 +993,7 @@
fb.addProperty(attendee)
fb.addProperty(Property("DTSTART", timerange.start))
fb.addProperty(Property("DTEND", timerange.end))
- fb.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
if len(fbinfo[0]) != 0:
fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
if len(fbinfo[1]) != 0:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,7 +31,9 @@
from twext.web2.stream import MemoryStream
from twistedcaldav import caldavxml
+from twistedcaldav.ical import Component
from twistedcaldav.method import report_common
+from twistedcaldav.util import bestAcceptType
from txdav.caldav.icalendarstore import TimeRangeLowerLimit, TimeRangeUpperLimit
from txdav.xml import element as davxml
@@ -60,6 +62,11 @@
matchcount = [0]
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
+
def generateFreeBusyInfo(calresource, uri): #@UnusedVariable
"""
Run a free busy report on the specified calendar collection
@@ -104,7 +111,7 @@
fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)
response = Response()
- response.stream = MemoryStream(str(fbcalendar))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
+ response.stream = MemoryStream(fbcalendar.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
returnValue(response)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -45,11 +45,12 @@
"""
name = "mkcol"
- allowed_children = { (davxml.dav_namespace, "set"): (0, 1) }
+ allowed_children = {(davxml.dav_namespace, "set"): (0, 1)}
- child_types = { "WebDAVUnknownElement": (0, None) }
+ child_types = {"WebDAVUnknownElement": (0, None)}
+
@registerElement
class MakeCollectionResponse (davxml.WebDAVElement):
"""
@@ -58,4 +59,4 @@
"""
name = "mkcol-response"
- allowed_children = { davxml.WebDAVElement: (0, None) }
+ allowed_children = {davxml.WebDAVElement: (0, None)}
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -48,43 +48,53 @@
self._parent = parent
CalDAVResource.__init__(self)
+
def principalCollections(self):
return self._parent.principalCollections()
+
def isCollection(self):
return False
+
def resourceName(self):
raise NotImplementedError
-
+
+
def http_PUT(self, request):
return responsecode.FORBIDDEN
+
@inlineCallbacks
def http_DELETE(self, request):
-
+
response = (yield super(NotificationResource, self).http_DELETE(request))
if response == responsecode.NO_CONTENT:
yield self._parent.removedNotifictionMessage(request, self.resourceName())
returnValue(response)
-
+
+
+
class NotificationCollectionResource(ReadOnlyNoCopyResourceMixIn, CalDAVResource):
def notificationsDB(self):
-
+
if not hasattr(self, "_notificationsDB"):
self._notificationsDB = NotificationsDatabase(self)
return self._notificationsDB
+
def isCollection(self):
return True
+
def resourceType(self):
return davxml.ResourceType.notification
+
@inlineCallbacks
def addNotification(self, request, uid, xmltype, xmldata):
-
+
# Write data to file
rname = uid + ".xml"
yield self._writeNotification(request, uid, rname, xmltype, xmldata)
@@ -103,7 +113,7 @@
@inlineCallbacks
def deleteNotifictionMessageByUID(self, request, uid):
-
+
# See if it exists and delete the resource
record = yield self.notificationsDB().recordForUID(uid)
if record:
@@ -117,7 +127,7 @@
record = yield self.notificationsDB().recordForName(rname)
if record:
yield self.deleteNotification(request, record)
-
+
returnValue(None)
@@ -131,13 +141,16 @@
return maybeDeferred(self.notificationsDB().removeRecordForName, rname)
+
class NotificationRecord(object):
-
+
def __init__(self, uid, name, xmltype):
self.uid = uid
self.name = name
self.xmltype = xmltype
+
+
class NotificationsDatabase(AbstractSQLDatabase):
log = Logger()
@@ -154,35 +167,40 @@
db_filename = os.path.join(self.resource.fp.path, NotificationsDatabase.db_basename)
super(NotificationsDatabase, self).__init__(db_filename, True, autocommit=True)
+
def allRecords(self):
-
+
records = self._db_execute("select * from NOTIFICATIONS")
return [self._makeRecord(row) for row in (records if records is not None else ())]
-
+
+
def recordForUID(self, uid):
-
+
row = self._db_execute("select * from NOTIFICATIONS where UID = :1", uid)
return self._makeRecord(row[0]) if row else None
-
+
+
def addOrUpdateRecord(self, record):
self._db_execute("""insert or replace into NOTIFICATIONS (UID, NAME, TYPE)
values (:1, :2, :3)
""", record.uid, record.name, record.xmltype,
)
-
+
self._db_execute(
"""
insert or replace into REVISIONS (NAME, REVISION, DELETED)
values (:1, :2, :3)
""", record.name, self.bumpRevision(fast=True), 'N',
)
-
+
+
def removeRecordForUID(self, uid):
record = self.recordForUID(uid)
self.removeRecordForName(record.name)
-
+
+
def removeRecordForName(self, rname):
self._db_execute("delete from NOTIFICATIONS where NAME = :1", rname)
@@ -192,12 +210,13 @@
where NAME = :3
""", self.bumpRevision(fast=True), 'Y', rname
)
-
+
+
def whatchanged(self, revision):
results = [(name.encode("utf-8"), deleted) for name, deleted in self._db_execute("select NAME, DELETED from REVISIONS where REVISION > :1", revision)]
- results.sort(key=lambda x:x[1])
-
+ results.sort(key=lambda x: x[1])
+
changed = []
deleted = []
for name, wasdeleted in results:
@@ -209,14 +228,16 @@
changed.append(name)
else:
raise SyncTokenValidException
-
+
return changed, deleted,
+
def lastRevision(self):
return self._db_value_for_sql(
"select REVISION from REVISION_SEQUENCE"
)
+
def bumpRevision(self, fast=False):
self._db_execute(
"""
@@ -230,18 +251,21 @@
""",
)
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
return NotificationsDatabase.schema_version
+
def _db_type(self):
"""
@return: the collection type assigned to this index.
"""
return NotificationsDatabase.db_type
+
def _db_init_data_tables(self, q):
"""
Initialise the underlying database tables.
@@ -302,6 +326,7 @@
"""
)
+
def _db_upgrade_data_tables(self, q, old_version):
"""
Upgrade the data from an older version of the DB.
@@ -310,7 +335,7 @@
# Nothing to do as we have not changed the schema
pass
+
def _makeRecord(self, row):
-
+
return NotificationRecord(*[str(item) if type(item) == types.UnicodeType else item for item in row])
-
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -28,8 +28,8 @@
from twistedcaldav.dateops import timeRangesOverlap
from twistedcaldav.ical import Component, Property
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
log = Logger()
@@ -89,7 +89,7 @@
instances = None
else:
# Expand the instances up to infinity
- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ignoreInvalidInstances=True)
+ instances = component.expandTimeRanges(DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)), ignoreInvalidInstances=True)
else:
instances = component.expandTimeRanges(maxend, ignoreInvalidInstances=True)
else:
@@ -117,7 +117,7 @@
Set the default timezone to use with this query.
@param calendar: a L{Component} for the VCALENDAR containing the one
VTIMEZONE that we want
- @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
+ @return: the L{Timezone} derived from the VTIMEZONE or utc.
"""
if tzelement is None:
@@ -127,7 +127,7 @@
elif isinstance(tzelement, Component):
tz = tzelement.gettimezone()
if tz is None:
- tz = PyCalendarTimezone(utc=True)
+ tz = Timezone(utc=True)
self.child.settzinfo(tz)
return tz
@@ -357,7 +357,7 @@
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{PyCalendarTimezone} to use.
+ @param tzinfo: a L{Timezone} to use.
"""
# Give tzinfo to any TimeRange we have
@@ -374,7 +374,7 @@
Get the date farthest into the future in any time-range elements
@param currentMaximum: current future value to compare with
- @type currentMaximum: L{PyCalendarDateTime}
+ @type currentMaximum: L{DateTime}
"""
# Give tzinfo to any TimeRange we have
@@ -471,7 +471,7 @@
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{PyCalendarTimezone} to use.
+ @param tzinfo: a L{Timezone} to use.
"""
# Give tzinfo to any TimeRange we have
@@ -484,7 +484,7 @@
Get the date farthest into the future in any time-range elements
@param currentMaximum: current future value to compare with
- @type currentMaximum: L{PyCalendarDateTime}
+ @type currentMaximum: L{DateTime}
"""
# Give tzinfo to any TimeRange we have
@@ -652,15 +652,15 @@
if "start" not in xml_element.attributes and "end" not in xml_element.attributes:
raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
- self.start = PyCalendarDateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
- self.end = PyCalendarDateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
+ self.start = DateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
+ self.end = DateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
self.tzinfo = None
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{PyCalendarTimezone} to use.
+ @param tzinfo: a L{Timezone} to use.
"""
# Give tzinfo to any TimeRange we have
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -17,7 +17,7 @@
from twistedcaldav import caldavxml
from twistedcaldav.query import calendarqueryfilter
import twistedcaldav.test.util
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.timezone import Timezone
from twistedcaldav.query.calendarquery import sqlcalendarquery
class Tests(twistedcaldav.test.util.TestCase):
@@ -36,7 +36,7 @@
)
)
filter = calendarqueryfilter.Filter(filter)
- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
sql, args = sqlcalendarquery(filter)
self.assertTrue(sql.find("RESOURCE") != -1)
@@ -60,7 +60,7 @@
)
)
filter = calendarqueryfilter.Filter(filter)
- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
sql, args = sqlcalendarquery(filter)
self.assertTrue(sql.find("RESOURCE") != -1)
@@ -88,7 +88,7 @@
)
)
filter = calendarqueryfilter.Filter(filter)
- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
sql, args = sqlcalendarquery(filter)
self.assertTrue(sql.find("RESOURCE") != -1)
@@ -119,7 +119,7 @@
)
)
filter = calendarqueryfilter.Filter(filter)
- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
sql, args = sqlcalendarquery(filter)
self.assertTrue(sql.find("RESOURCE") != -1)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,7 +18,7 @@
from twistedcaldav.query import calendarqueryfilter
import twistedcaldav.test.util
from twistedcaldav.caldavxml import TimeZone
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.timezone import Timezone
class Tests(twistedcaldav.test.util.TestCase):
@@ -227,4 +227,4 @@
END:VCALENDAR
"""))
- self.assertTrue(isinstance(tz, PyCalendarTimezone))
+ self.assertTrue(isinstance(tz, Timezone))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -71,6 +71,7 @@
from twistedcaldav.directory.internal import InternalDirectoryRecord
from twistedcaldav.extensions import DAVResource, DAVPrincipalResource, \
DAVResourceWithChildrenMixin
+from twistedcaldav import ical
from twistedcaldav.ical import Component
from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
@@ -257,10 +258,11 @@
# Redirect to include trailing '/' in URI
return RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/') + '/'))
- def _defer(data):
+ def _defer(result):
+ data, accepted_type = result
response = Response()
- response.stream = MemoryStream(str(data))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
+ response.stream = MemoryStream(data.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
return response
d = self.iCalendarRolledup(request)
@@ -375,7 +377,7 @@
(self,))
- def storeStream(self, stream):
+ def storeStream(self, stream, format):
"""
Store the content of the stream in this resource, as it would via a PUT.
@@ -385,8 +387,7 @@
@return: a L{Deferred} which fires with an HTTP response.
@rtype: L{Deferred}
"""
- raise NotImplementedError("%s does not implement storeStream" %
- (self,))
+ raise NotImplementedError("%s does not implement storeStream" % (self,))
# End transitional new-store interface
@@ -605,12 +606,21 @@
returnValue(self.getSupportedComponentSet())
elif qname == caldavxml.SupportedCalendarData.qname() and self.isPseudoCalendarCollection():
- returnValue(caldavxml.SupportedCalendarData(
+ dataTypes = []
+ dataTypes.append(
caldavxml.CalendarData(**{
"content-type": "text/calendar",
"version" : "2.0",
}),
- ))
+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ caldavxml.CalendarData(**{
+ "content-type": "application/calendar+json",
+ "version" : "2.0",
+ }),
+ )
+ returnValue(caldavxml.SupportedCalendarData(*dataTypes))
elif qname == caldavxml.MaxResourceSize.qname() and self.isPseudoCalendarCollection():
if config.MaxResourceSize:
@@ -642,12 +652,21 @@
elif qname == carddavxml.SupportedAddressData.qname() and self.isAddressBookCollection():
# CardDAV, section 6.2.2
- returnValue(carddavxml.SupportedAddressData(
+ dataTypes = []
+ dataTypes.append(
carddavxml.AddressDataType(**{
"content-type": "text/vcard",
"version" : "3.0",
}),
- ))
+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ carddavxml.AddressDataType(**{
+ "content-type": "application/vcard+json",
+ "version" : "3.0",
+ }),
+ )
+ returnValue(carddavxml.SupportedAddressData(*dataTypes))
elif qname == carddavxml.MaxResourceSize.qname() and self.isAddressBookCollection() and not self.isDirectoryBackedAddressBookCollection():
# CardDAV, section 6.2.3
@@ -1446,11 +1465,6 @@
returnValue(caldata)
- def iCalendarText(self):
- # storebridge handles this method
- raise NotImplementedError()
-
-
def iCalendar(self):
# storebridge handles this method
raise NotImplementedError()
@@ -2446,18 +2460,13 @@
if qname == caldavxml.SupportedCalendarComponentSets.qname():
if config.RestrictCalendarsToOneComponentType:
- prop = caldavxml.SupportedCalendarComponentSets(
+ prop = caldavxml.SupportedCalendarComponentSets(*[
caldavxml.SupportedCalendarComponentSet(
caldavxml.CalendarComponent(
- name="VEVENT",
+ name=name,
),
- ),
- caldavxml.SupportedCalendarComponentSet(
- caldavxml.CalendarComponent(
- name="VTODO",
- ),
- ),
- )
+ ) for name in ical.allowedStoreComponents
+ ])
else:
prop = caldavxml.SupportedCalendarComponentSets()
returnValue(prop)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -45,7 +45,7 @@
from twistedcaldav import caldavxml, customxml
from twistedcaldav.caldavxml import caldav_namespace, CalendarFreeBusySet
from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.ical import allowedComponents, Component
+from twistedcaldav.ical import Component, allowedSchedulingComponents
from twistedcaldav.resource import CalDAVResource
from twistedcaldav.resource import isCalendarCollectionResource
@@ -300,8 +300,14 @@
"""
Write either the default VEVENT or VTODO calendar property, validating and canonicalizing the value
"""
- tasks = property.qname() == customxml.ScheduleDefaultTasksURL
- error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL") if tasks else (caldav_namespace, "valid-schedule-default-calendar-URL")
+ if property.qname() == caldavxml.ScheduleDefaultCalendarURL.qname():
+ ctype = "VEVENT"
+ error_element = (caldav_namespace, "valid-schedule-default-calendar-URL")
+ elif property.qname() == customxml.ScheduleDefaultTasksURL.qname():
+ ctype = "VTODO"
+ error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL")
+ else:
+ returnValue(None)
# Verify that the calendar added in the PROPPATCH is valid.
property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
@@ -325,7 +331,7 @@
try:
# Now set it on the new store object
- yield self.parent._newStoreHome.setDefaultCalendar(cal._newStoreObject, tasks)
+ yield self.parent._newStoreHome.setDefaultCalendar(cal._newStoreObject, ctype)
except InvalidDefaultCalendar as e:
raise HTTPError(ErrorResponse(
responsecode.CONFLICT,
@@ -390,7 +396,7 @@
def getSupportedComponentSet(self):
return caldavxml.SupportedCalendarComponentSet(
- *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
+ *[caldavxml.CalendarComponent(name=item) for item in allowedSchedulingComponents]
)
@@ -407,7 +413,7 @@
# Check authentication and access controls
yield self.authorize(request, (caldavxml.ScheduleSend(),))
- calendar = (yield self.loadCalendarFromRequest(request))
+ calendar, format = (yield self.loadCalendarFromRequest(request))
originator = (yield self.loadOriginatorFromRequestDetails(request))
recipients = self.loadRecipientsFromCalendarData(calendar)
@@ -431,14 +437,25 @@
# Do the POST processing treating
result = (yield scheduler.doSchedulingViaPOST(originator, recipients, calendar))
- returnValue(result.response())
+ returnValue(result.response(format=format))
+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in Component.allowedTypes() else None
+
+
@inlineCallbacks
def loadCalendarFromRequest(self, request):
# Must be content-type text/calendar
contentType = request.headers.getHeader("content-type")
- if contentType is not None and (contentType.mediaType, contentType.mediaSubtype) != ("text", "calendar"):
+ format = self.determineType(contentType)
+ if format is None:
self.log.error("MIME type %s not allowed in calendar collection" % (contentType,))
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
@@ -448,7 +465,7 @@
# Parse the calendar object from the HTTP request stream
try:
- calendar = (yield Component.fromIStream(request.stream))
+ calendar = (yield Component.fromIStream(request.stream, format=format))
except:
# FIXME: Bare except
self.log.error("Error while handling POST: %s" % (Failure(),))
@@ -458,7 +475,7 @@
description="Can't parse calendar data"
))
- returnValue(calendar)
+ returnValue((calendar, format,))
@inlineCallbacks
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -46,7 +46,7 @@
from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
from twistedcaldav.linkresource import LinkFollowerMixIn
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
# FIXME: Get rid of these imports
@@ -740,7 +740,7 @@
typeAttr = {'shared-type': self.sharedResourceType()}
xmltype = customxml.InviteNotification(**typeAttr)
xmldata = customxml.Notification(
- customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
+ customxml.DTStamp.fromString(DateTime.getNowUTC().getText()),
customxml.InviteNotification(
customxml.UID.fromString(invitation.uid()),
element.HRef.fromString(userid),
@@ -1362,7 +1362,7 @@
record = shareePrincipal.record
xmldata = customxml.Notification(
- customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
+ customxml.DTStamp.fromString(DateTime.getNowUTC().getText()),
customxml.InviteReply(
*(
(
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,7 +18,7 @@
Generic SQL database access object.
"""
-__all__ = [
+__all__ = [
"db_prefix",
"DatabaseError",
"AbstractSQLDatabase",
@@ -50,7 +50,7 @@
def __init__(self, dbpath, persistent, autocommit=False):
"""
-
+
@param dbpath: the path where the db file is stored.
@type dbpath: str
@param persistent: C{True} if the data in the DB must be perserved during upgrades,
@@ -63,21 +63,25 @@
self.persistent = persistent
self.autocommit = autocommit
+
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.dbpath)
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
"""
raise NotImplementedError
-
+
+
def _db_type(self):
"""
@return: the collection type assigned to this index.
"""
raise NotImplementedError
-
+
+
def _db(self):
"""
Access the underlying database.
@@ -108,7 +112,7 @@
# Create CALDAV table if needed
if self._test_schema_table(q):
-
+
version, dbtype = self._get_schema_version(q)
if (version != self._db_version()) or (dbtype != self._db_type()):
@@ -130,7 +134,7 @@
if version != self._db_version():
log.error("Database %s has different schema (v.%s vs. v.%s)"
% (db_filename, version, self._db_version()))
-
+
# Upgrade the DB
return self._db_upgrade(version)
@@ -139,9 +143,11 @@
self._db_connection.commit()
finally:
- if q is not None: q.close()
+ if q is not None:
+ q.close()
return self._db_connection
+
def _test_schema_table(self, q):
q.execute("""
select (1) from SQLITE_MASTER
@@ -149,6 +155,7 @@
""")
return q.fetchone()
+
def _get_schema_version(self, q):
q.execute(
"""
@@ -157,7 +164,8 @@
""")
version = q.fetchone()
- if version is not None: version = version[0]
+ if version is not None:
+ version = version[0]
q.execute(
"""
@@ -166,10 +174,12 @@
""")
dbtype = q.fetchone()
- if dbtype is not None: dbtype = dbtype[0]
+ if dbtype is not None:
+ dbtype = dbtype[0]
return version, dbtype
+
def _db_init(self, db_filename, q):
"""
Initialise the underlying database tables.
@@ -183,7 +193,7 @@
old_isolation = self._db_connection.isolation_level
self._db_connection.isolation_level = None
q.execute("begin exclusive transaction")
-
+
# We re-check whether the schema table is present again AFTER we've got an exclusive
# lock as some other server process may have snuck in and already created it
# before we got the lock, or whilst we were waiting for it.
@@ -199,6 +209,7 @@
self._db_connection.isolation_level = old_isolation
+
def _db_init_schema_table(self, q):
"""
Initialise the underlying database tables.
@@ -229,6 +240,7 @@
""", [self._db_type()]
)
+
def _db_init_data_tables(self, q):
"""
Initialise the underlying database tables.
@@ -237,6 +249,7 @@
"""
raise NotImplementedError
+
def _db_recreate(self, do_commit=True):
"""
Recreate the database tables.
@@ -246,15 +259,17 @@
if do_commit:
self._db_commit()
+
def _db_can_upgrade(self, old_version):
-
+
return self.persistent
+
def _db_upgrade(self, old_version):
"""
Upgrade the database tables.
"""
-
+
if self._db_can_upgrade(old_version):
self._db_connection = sqlite.connect(self.dbpath, isolation_level=None)
q = self._db_connection.cursor()
@@ -278,6 +293,7 @@
return self._db()
+
def _db_upgrade_data_tables(self, q, old_version):
"""
Upgrade the data from an older version of the DB.
@@ -286,6 +302,7 @@
# cannot be thrown away.
raise NotImplementedError("Persistent databases MUST support an upgrade method.")
+
def _db_upgrade_schema(self, q):
"""
Upgrade the stored schema version to the current one.
@@ -297,11 +314,13 @@
""", [self._db_version()]
)
+
def _db_close(self):
if hasattr(self, "_db_connection"):
self._db_connection.close()
del self._db_connection
+
def _db_values_for_sql(self, sql, *query_params):
"""
Execute an SQL query and obtain the resulting values.
@@ -313,6 +332,7 @@
"""
return (row[0] for row in self._db_execute(sql, *query_params))
+
def _db_value_for_sql(self, sql, *query_params):
"""
Execute an SQL query and obtain a single value.
@@ -328,6 +348,7 @@
value = row
return value
+
def _db_execute(self, sql, *query_params):
"""
Execute an SQL query and obtain the resulting values.
@@ -347,5 +368,10 @@
finally:
q.close()
- def _db_commit (self): self._db_connection.commit()
- def _db_rollback(self): self._db_connection.rollback()
+
+ def _db_commit(self):
+ self._db_connection.commit()
+
+
+ def _db_rollback(self):
+ self._db_connection.rollback()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -36,6 +36,7 @@
from twistedcaldav.util import computeProcessCount
from calendarserver.push.util import getAPNTopicFromCertificate
+from twistedcaldav import ical
log = Logger()
@@ -156,9 +157,8 @@
"resourceInfoAttr": None, # contains location/resource info
"autoAcceptGroupAttr": None, # auto accept group
},
- "partitionSchema": {
+ "poddingSchema": {
"serverIdAttr": None, # maps to augments server-id
- "partitionIdAttr": None, # maps to augments partition-id
},
},
}
@@ -304,7 +304,7 @@
# the master process, rather than having
# each client make its connections directly.
- "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility tools
+ "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility
# tools from running if the database needs a schema
# upgrade.
"StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists in ConfigRoot, stop
@@ -546,6 +546,11 @@
"EnableManagedAttachments" : False, # Support Managed Attachments
#
+ # Generic CalDAV/CardDAV extensions
+ #
+ "EnableJSONData" : True, # Allow clients to send/receive JSON jCal and jCard format data
+
+ #
# Non-standard CalDAV extensions
#
"EnableDropBox" : False, # Calendar Drop Box
@@ -595,6 +600,12 @@
# If on, it will also cause new accounts to provision with separate
# calendars for events and tasks.
+ "SupportedComponents" : [ # Set of supported iCalendar components
+ "VEVENT",
+ "VTODO",
+ #"VPOLL",
+ ],
+
"ParallelUpgrades" : False, # Perform upgrades - currently only the
# database -> filesystem migration - but in
# the future, hopefully all relevant
@@ -806,11 +817,11 @@
# Support multiple hosts within a domain
#
"Servers" : {
- "Enabled": False, # Multiple servers/partitions enabled or not
- "ConfigFile": "localservers.xml", # File path for server information
- "MaxClients": 5, # Pool size for connections to each partition
+ "Enabled": False, # Multiple servers enabled or not
+ "ConfigFile": "localservers.xml", # File path for server information
+ "MaxClients": 5, # Pool size for connections to between servers
+ "InboxName": "podding", # Name for top-level inbox resource
},
- "ServerPartitionID": "", # Unique ID for this server's partition instance.
#
# Performance tuning
@@ -881,7 +892,8 @@
# Support for Content-Encoding compression options as specified in
# RFC2616 Section 3.5
- "ResponseCompression": True,
+ # Defaults off, because it weakens TLS (CRIME attack).
+ "ResponseCompression": False,
# The retry-after value (in seconds) to return with a 503 error
"HTTPRetryAfter": 180,
@@ -1011,7 +1023,8 @@
# means no automatic shutdown.
"AgentInactivityTimeoutSeconds" : 4 * 60 * 60,
- # These two aren't relative to ConfigRoot:
+ # These aren't relative to ConfigRoot:
+ "ImportConfig": "", # Config to read first and merge
"Includes": [], # Other plists to parse after this one
"WritableConfigFile" : "", # which config file calendarserver_config should
# write to for changes; empty string means the main config file.
@@ -1043,18 +1056,41 @@
if self._configFileName:
configDict = self._parseConfigFromFile(self._configFileName)
configDict = ConfigDict(configDict)
- # Now check for Includes and parse and add each of those
- if "Includes" in configDict:
- for include in configDict.Includes:
- # Includes are not relative to ConfigRoot
- path = _expandPath(include)
+
+ def _loadImport(childDict):
+ # Look for an import and read that one as the main config and merge the current one into that
+ if "ImportConfig" in childDict and childDict.ImportConfig:
+ configRoot = os.path.join(childDict.ServerRoot, childDict.ConfigRoot)
+ path = _expandPath(fullServerPath(configRoot, childDict.ImportConfig))
if os.path.exists(path):
- additionalDict = ConfigDict(self._parseConfigFromFile(path))
- if additionalDict:
- log.info("Adding configuration from file: '%s'" % (path,))
- mergeData(configDict, additionalDict)
- else:
- log.debug("Missing configuration file: '%s'" % (path,))
+ importDict = ConfigDict(self._parseConfigFromFile(path))
+ if importDict:
+ self.importedFiles.append(path)
+ importDict = _loadImport(importDict)
+ mergeData(importDict, childDict)
+ return importDict
+ raise ConfigurationError("Import configuration file '{path}' must exist and be valid.".format(path=path))
+ else:
+ return childDict
+
+ def _loadIncludes(parentDict):
+ # Now check for Includes and parse and add each of those
+ if "Includes" in parentDict:
+ configRoot = os.path.join(parentDict.ServerRoot, parentDict.ConfigRoot)
+ for include in parentDict.Includes:
+ # Includes are not relative to ConfigRoot
+ path = _expandPath(fullServerPath(configRoot, include))
+ if os.path.exists(path):
+ additionalDict = ConfigDict(self._parseConfigFromFile(path))
+ if additionalDict:
+ self.includedFiles.append(path)
+ _loadIncludes(additionalDict)
+ mergeData(parentDict, additionalDict)
+ else:
+ self.missingFiles.append(path)
+
+ configDict = _loadImport(configDict)
+ _loadIncludes(configDict)
return configDict
@@ -1479,6 +1515,14 @@
+def _updateICalendar(configDict, reloading=False):
+ """
+ Updated support iCalendar components.
+ """
+ ical._updateAllowedComponents(tuple(configDict.SupportedComponents))
+
+
+
def _updateScheduling(configDict, reloading=False):
#
# Scheduling
@@ -1518,8 +1562,7 @@
from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
if configDict.Servers.Enabled:
Servers.load()
- Servers.getThisServer().installReverseProxies(
- configDict.ServerPartitionID,
+ Servers.installReverseProxies(
configDict.Servers.MaxClients,
)
else:
@@ -1590,6 +1633,7 @@
_updateRejectClients,
_updateLogLevels,
_updateNotifications,
+ _updateICalendar,
_updateScheduling,
_updateServers,
_updateCompliance,
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,7 +15,7 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.python.log import Logger
from twext.web2.dav.http import ErrorResponse, ResponseQueue, MultiStatusResponse
@@ -36,14 +36,14 @@
from twisted.python.hashlib import md5
from twisted.python.util import FancyEqMixin
-from twistedcaldav import customxml, carddavxml, caldavxml
+from twistedcaldav import customxml, carddavxml, caldavxml, ical
from twistedcaldav.caldavxml import caldav_namespace, MaxAttendeesPerInstance, \
MaxInstances, NoUIDConflict
from twistedcaldav.carddavxml import carddav_namespace, NoUIDConflict as NovCardUIDConflict
from twistedcaldav.config import config
from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
from twistedcaldav.ical import Component as VCalendar, Property as VProperty, \
- InvalidICalendarDataError, iCalendarProductID, allowedComponents, Component
+ InvalidICalendarDataError, iCalendarProductID, Component
from twistedcaldav.memcachelock import MemcacheLockTimeoutError
from twistedcaldav.notifications import NotificationCollectionResource, NotificationResource
from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource, \
@@ -82,6 +82,7 @@
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.instance import InvalidOverriddenInstanceError, \
TooManyInstancesError
+from twistedcaldav.util import bestAcceptType
import collections
"""
@@ -626,14 +627,17 @@
# Read in all data
data = (yield allDataFromStream(request.stream))
- components = self.componentsFromData(data)
+ format = request.headers.getHeader("content-type")
+ if format:
+ format = "%s/%s" % (format.mediaType, format.mediaSubtype,)
+ components = self.componentsFromData(data, format)
if components is None:
raise HTTPError(StatusResponse(BAD_REQUEST, "Could not parse valid data from request body"))
# Build response
xmlresponses = [None] * len(components)
indexedComponents = [idxComponent for idxComponent in enumerate(components)]
- yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses)
+ yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses, format)
result = MultiStatusResponse(xmlresponses)
@@ -650,7 +654,7 @@
@inlineCallbacks
- def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses):
+ def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses, format):
"""
Do create from simpleBatchPOST or crudCreate()
Subclasses may override
@@ -664,7 +668,7 @@
# Get a resource for the new item
newchildURL = joinURL(request.path, name)
newchild = (yield request.locateResource(newchildURL))
- changedData = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
+ changedComponent = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
except HTTPError, e:
# Extract the pre-condition
@@ -674,30 +678,30 @@
error = (error.namespace, error.name,)
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error, format)
)
except Exception:
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code=BAD_REQUEST, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, BAD_REQUEST, None, format)
)
else:
if not return_changed:
- changedData = None
+ changedComponent = None
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
)
@inlineCallbacks
- def bulkCreateResponse(self, component, newchildURL, newchild, changedData, code, error):
+ def bulkCreateResponse(self, component, newchildURL, newchild, changedComponent, code, error, format):
"""
generate one xmlresponse for bulk create
"""
if code is None:
etag = (yield newchild.etag())
- if changedData is None:
+ if changedComponent is None:
returnValue(
davxml.PropertyStatusResponse(
davxml.HRef.fromString(newchildURL),
@@ -717,7 +721,7 @@
davxml.PropertyStatus(
davxml.PropertyContainer(
davxml.GETETag.fromString(etag.generate()),
- self.xmlDataElementType().fromTextData(changedData),
+ self.xmlDataElementType().fromComponent(changedComponent, format),
),
davxml.Status.fromResponseCode(OK),
)
@@ -822,6 +826,7 @@
for index, xmldata in crudCreateInfo:
component = xmldata.generateComponent()
+ format = xmldata.content_type
if hasPrivilege is not True:
e = hasPrivilege # use same code pattern as exception
@@ -830,13 +835,13 @@
error = e.response.error
error = (error.namespace, error.name,)
- xmlresponse = yield self.bulkCreateResponse(component, None, None, None, code, error)
+ xmlresponse = yield self.bulkCreateResponse(component, None, None, None, code, error, format)
xmlresponses[index] = xmlresponse
else:
indexedComponents.append((index, component,))
- yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses)
+ yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses, format)
@inlineCallbacks
@@ -847,8 +852,8 @@
code = None
error = None
try:
- componentdata = xmldata.textData()
component = xmldata.generateComponent()
+ format = xmldata.content_type
updateResource = (yield request.locateResource(href))
if not updateResource.exists():
@@ -862,7 +867,7 @@
if ifmatch and ifmatch != etag.generate():
raise HTTPError(PRECONDITION_FAILED)
- changedData = yield self.storeResourceData(updateResource, component, componentdata)
+ changedComponent = yield self.storeResourceData(updateResource, component, returnChangedData=return_changed)
except HTTPError, e:
# Extract the pre-condition
@@ -875,7 +880,7 @@
code = BAD_REQUEST
if code is None:
- if not return_changed or changedData is None:
+ if changedComponent is None:
xmlresponses[index] = davxml.PropertyStatusResponse(
davxml.HRef.fromString(href),
davxml.PropertyStatus(
@@ -891,7 +896,7 @@
davxml.PropertyStatus(
davxml.PropertyContainer(
davxml.GETETag.fromString(etag.generate()),
- self.xmlDataElementType().fromTextData(changedData),
+ self.xmlDataElementType().fromComponentData(changedComponent, format),
),
davxml.Status.fromResponseCode(OK),
)
@@ -989,7 +994,7 @@
if comps:
comps = comps.split(",")
else:
- comps = allowedComponents
+ comps = ical.allowedStoreComponents
return caldavxml.SupportedCalendarComponentSet(
*[caldavxml.CalendarComponent(name=item) for item in comps]
)
@@ -1016,7 +1021,7 @@
if comps:
comps = comps.split(",")
else:
- comps = allowedComponents
+ comps = ical.allowedStoreComponents
return comps
@@ -1053,6 +1058,8 @@
if config.EnableBatchUpload:
self._postHandlers[("text", "calendar")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
+ if config.EnableJSONData:
+ self._postHandlers[("application", "calendar+json")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
self.xmlDocHandlers[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
@@ -1088,6 +1095,11 @@
def iCalendarRolledup(self, request):
# FIXME: uncached: implement cache in the storage layer
+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
# Generate a monolithic calendar
calendar = VCalendar("VCALENDAR")
calendar.addProperty(VProperty("VERSION", "2.0"))
@@ -1138,17 +1150,13 @@
calendar.addComponent(component)
- # Cache the data
- data = str(calendar)
- data = (yield self.getInternalSyncToken()) + "\r\n" + data
+ returnValue((calendar, accepted_type,))
- returnValue(calendar)
-
createCalendarCollection = _CommonHomeChildCollectionMixin.createCollection
@classmethod
- def componentsFromData(cls, data):
+ def componentsFromData(cls, data, format):
"""
Need to split a single VCALENDAR into separate ones based on UID with the
appropriate VTIEMZONES included.
@@ -1158,7 +1166,7 @@
# Split into components by UID and TZID
try:
- vcal = VCalendar.fromString(data)
+ vcal = VCalendar.fromString(data, format)
except InvalidICalendarDataError:
return None
@@ -1242,7 +1250,8 @@
elif qname == caldavxml.CalendarTimeZone.qname():
timezone = self._newStoreObject.getTimezone()
- returnValue(caldavxml.CalendarTimeZone.fromString(str(timezone)) if timezone else None)
+ format = property.content_type if isinstance(property, caldavxml.CalendarTimeZone) else None
+ returnValue(caldavxml.CalendarTimeZone.fromCalendar(timezone, format=format) if timezone else None)
result = (yield super(CalendarCollectionResource, self).readProperty(property, request))
returnValue(result)
@@ -1288,7 +1297,7 @@
yield newchild.storeComponent(component)
if returnChangedData and newchild._newStoreObject._componentChanged:
result = (yield newchild.componentForUser())
- returnValue(str(result))
+ returnValue(result)
else:
returnValue(None)
@@ -2210,16 +2219,38 @@
return self._newStoreObject.component()
+ def allowedTypes(self):
+ """
+ Return a dict of allowed MIME types for storing, mapped to equivalent PyCalendar types.
+ """
+ raise NotImplementedError
+
+
+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in self.allowedTypes() else None
+
+
@inlineCallbacks
def render(self, request):
if not self.exists():
log.debug("Resource not found: %s" % (self,))
raise HTTPError(NOT_FOUND)
+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), self.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
output = yield self.component()
- response = Response(OK, {}, str(output))
- response.headers.setHeader("content-type", self.contentType())
+ response = Response(OK, {}, output.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
returnValue(response)
@@ -2379,10 +2410,10 @@
@inlineCallbacks
- def storeStream(self, stream):
+ def storeStream(self, stream, format):
# FIXME: direct tests
- component = self._componentFromStream((yield allDataFromStream(stream)))
+ component = self._componentFromStream((yield allDataFromStream(stream)), format)
result = (yield self.storeComponent(component))
returnValue(result)
@@ -2514,6 +2545,13 @@
_componentFromStream = VCalendar.fromString
+ def allowedTypes(self):
+ """
+ Return a tuple of allowed MIME types for storing.
+ """
+ return Component.allowedTypes()
+
+
@inlineCallbacks
def inNewTransaction(self, request, label=""):
"""
@@ -2541,12 +2579,6 @@
self._initializeWithObject(newObject, newParent)
returnValue(txn)
-
- @inlineCallbacks
- def iCalendarText(self):
- data = yield self.iCalendar()
- returnValue(str(data))
-
iCalendar = _CommonObjectResource.component
@@ -2710,7 +2742,8 @@
# Content-type check
content_type = request.headers.getHeader("content-type")
- if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "calendar"):
+ format = self.determineType(content_type)
+ if format is None:
log.error("MIME type {content_type} not allowed in calendar collection", content_type=content_type)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
@@ -2745,13 +2778,13 @@
))
try:
- component = Component.fromString(calendardata)
+ component = Component.fromString(calendardata, format)
except ValueError, e:
log.error(str(e))
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-calendar-data"),
- "Can't parse calendar data"
+ "Can't parse calendar data: %s" % (str(e),)
))
# storeComponent needs to know who the auth'd user is for access control
@@ -2864,7 +2897,7 @@
if rids is not None:
rids = rids[0].split(",")
try:
- rids = [PyCalendarDateTime.parseText(rid) if rid != "M" else None for rid in rids]
+ rids = [DateTime.parseText(rid) if rid != "M" else None for rid in rids]
except ValueError:
raise HTTPError(ErrorResponse(
FORBIDDEN,
@@ -3001,6 +3034,8 @@
if config.EnableBatchUpload:
self._postHandlers[("text", "vcard")] = AddressBookCollectionResource.simpleBatchPOST
+ if config.EnableJSONData:
+ self._postHandlers[("application", "vcard+json")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
self.xmlDocHandlers[customxml.Multiput] = AddressBookCollectionResource.crudBatchPOST
@@ -3032,9 +3067,9 @@
@classmethod
- def componentsFromData(cls, data):
+ def componentsFromData(cls, data, format):
try:
- return VCard.allFromString(data)
+ return VCard.allFromString(data, format)
except InvalidVCardDataError:
return None
@@ -3055,7 +3090,7 @@
yield newchild.storeComponent(component)
if returnChangedData and newchild._newStoreObject._componentChanged:
result = (yield newchild.component())
- returnValue(str(result))
+ returnValue(result)
else:
returnValue(None)
@@ -3096,7 +3131,7 @@
@inlineCallbacks
- def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses):
+ def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses, format):
"""
bulk create allowing groups to contain member UIDs added during the same bulk create
"""
@@ -3111,7 +3146,7 @@
# Get a resource for the new item
newchildURL = joinURL(request.path, name)
newchild = (yield request.locateResource(newchildURL))
- changedData = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
+ changedComponent = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
except GroupWithUnsharedAddressNotAllowedError, e:
# save off info and try again below
@@ -3126,20 +3161,20 @@
error = (error.namespace, error.name,)
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error, format)
)
except Exception:
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code=BAD_REQUEST, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, BAD_REQUEST, None, format)
)
else:
if not return_changed:
- changedData = None
+ changedComponent = None
coaddedUIDs |= set([component.resourceUID()])
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
)
if groupRetries:
@@ -3157,7 +3192,7 @@
# give FORBIDDEN response
index, component, newchildURL, newchild, missingUIDs = groupRetry
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData=None, code=FORBIDDEN, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, FORBIDDEN, None, format)
)
coaddedUIDs -= set([component.resourceUID()]) # group uid not added
groupRetries.remove(groupRetry) # remove this retry
@@ -3167,11 +3202,11 @@
newchild._metadata["coaddedUIDs"] = coaddedUIDs
# don't catch errors, abort the whole transaction
- changedData = yield self.storeResourceData(newchild, component, returnChangedData=return_changed)
+ changedComponent = yield self.storeResourceData(newchild, component, returnChangedData=return_changed)
if not return_changed:
- changedData = None
+ changedComponent = None
xmlresponses[index] = (
- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
)
@@ -3266,6 +3301,13 @@
_componentFromStream = VCard.fromString
+ def allowedTypes(self):
+ """
+ Return a tuple of allowed MIME types for storing.
+ """
+ return VCard.allowedTypes()
+
+
@inlineCallbacks
def vCardText(self):
data = yield self.vCard()
@@ -3340,7 +3382,8 @@
# Content-type check
content_type = request.headers.getHeader("content-type")
- if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "vcard"):
+ format = self.determineType(content_type)
+ if format is None:
log.error("MIME type {content_type} not allowed in vcard collection", content_type=content_type)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
@@ -3365,7 +3408,7 @@
))
try:
- component = VCard.fromString(vcarddata)
+ component = VCard.fromString(vcarddata, format)
except ValueError, e:
log.error(str(e))
raise HTTPError(ErrorResponse(
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,17 +19,19 @@
from twext.web2 import responsecode
from twext.web2.iweb import IResponse
from twext.web2.stream import MemoryStream
-from txdav.xml import element as davxml
from twext.web2.dav.util import davXMLFromStream, joinURL
+from twext.web2.http_headers import Headers, MimeType
from twistedcaldav import carddavxml
from twistedcaldav import vcard
-
from twistedcaldav.config import config
from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
+
from twisted.python.filepath import FilePath
from twisted.internet.defer import inlineCallbacks, returnValue
+from txdav.xml import element as davxml
+
class AddressBookMultiget (StoreTestCase):
"""
addressbook-multiget REPORT
@@ -214,7 +216,13 @@
'''
if data:
for filename, icaldata in data.iteritems():
- request = SimpleStoreRequest(self, "PUT", joinURL(addressbook_uri, filename + ".vcf"), authid="wsanchez")
+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(addressbook_uri, filename + ".vcf"),
+ headers=Headers({"content-type": MimeType.fromString("text/vcard")}),
+ authid="wsanchez"
+ )
request.stream = MemoryStream(icaldata)
yield self.send(request)
else:
@@ -222,7 +230,13 @@
for child in FilePath(self.vcards_dir).children():
if os.path.splitext(child.basename())[1] != ".vcf":
continue
- request = SimpleStoreRequest(self, "PUT", joinURL(addressbook_uri, child.basename()), authid="wsanchez")
+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(addressbook_uri, child.basename()),
+ headers=Headers({"content-type": MimeType.fromString("text/vcard")}),
+ authid="wsanchez"
+ )
request.stream = MemoryStream(child.getContent())
yield self.send(request)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,9 +14,16 @@
# limitations under the License.
##
+import twistedcaldav.test.util
from twistedcaldav import caldavxml
-import twistedcaldav.test.util
+from twistedcaldav.caldavxml import CalendarData
+from twistedcaldav.ical import normalize_iCalStr, Component
+def normalizeJSON(j):
+ return "".join(map(str.strip, j.splitlines())).replace(", ", ",").replace(": ", ":")
+
+
+
class CustomXML (twistedcaldav.test.util.TestCase):
@@ -50,3 +57,99 @@
tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z", end="20110202")
self.assertFalse(tr.valid())
+
+
+ def test_CalendarDataTextAndJSON(self):
+ """
+ Text that we can both parse and generate CalendarData elements with both text and json formats.
+ """
+ dataText = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+DTSTAMP:20080601T120000Z
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY;COUNT=400
+SUMMARY:Test
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ dataXML = """<?xml version='1.0' encoding='UTF-8'?>
+<calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (dataText,)
+
+ jsonText = """[
+ "vcalendar",
+ [
+ ["version", {}, "text", "2.0"],
+ ["prodid", {}, "text", "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"]
+ ],
+ [
+ ["vevent",
+ [
+ ["uid", {}, "text", "12345-67890"],
+ ["dtstart", {}, "date-time", "2008-06-01T12:00:00Z"],
+ ["dtend", {}, "date-time", "2008-06-01T13:00:00Z"],
+ ["attendee", {}, "cal-address", "mailto:user1 at example.com"],
+ ["attendee", {}, "cal-address", "mailto:user2 at example.com"],
+ ["dtstamp", {}, "date-time", "2008-06-01T12:00:00Z"],
+ ["exdate", {}, "date-time", "2008-06-02T12:00:00Z"],
+ ["exdate", {}, "date-time", "2008-06-03T12:00:00Z"],
+ ["organizer", {"cn": "User 01"}, "cal-address", "mailto:user1 at example.com"],
+ ["rrule", {}, "recur", {"count": 400, "freq": "DAILY"}],
+ ["summary", {}, "text", "Test"]
+ ],
+ [
+ ]
+ ]
+ ]
+]
+"""
+
+ jsonXML = """<?xml version='1.0' encoding='UTF-8'?>
+<calendar-data content-type='application/calendar+json' xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (jsonText,)
+
+ cd = CalendarData.fromTextData(dataText)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ comp = Component.fromString(dataText)
+ cd = CalendarData.fromCalendar(comp)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ cd = CalendarData.fromCalendar(comp, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))
+
+ cd = CalendarData.fromTextData(jsonText, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(cd.toxml(), jsonXML)
+
+ comp = Component.fromString(jsonText, format="application/calendar+json")
+ cd = CalendarData.fromCalendar(comp)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ cd = CalendarData.fromCalendar(comp, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -32,7 +32,7 @@
from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
from twisted.internet.defer import inlineCallbacks, returnValue
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twistedcaldav.ical import Component
from txdav.caldav.icalendarstore import ComponentUpdateState
from twistedcaldav.directory.directory import DirectoryService
@@ -124,8 +124,8 @@
)
query_timerange = caldavxml.TimeRange(
- start="%04d1001T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
- end="%04d1101T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
+ start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+ end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
)
query = caldavxml.CalendarQuery(
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -34,7 +34,7 @@
<dict>
<key>ResponseCompression</key>
- <false/>
+ <true/>
<key>HTTPPort</key>
<integer>8008</integer>
@@ -73,7 +73,7 @@
def _testResponseCompression(testCase):
- testCase.assertEquals(config.ResponseCompression, False)
+ testCase.assertEquals(config.ResponseCompression, True)
@@ -114,19 +114,19 @@
def testLoadConfig(self):
- self.assertEquals(config.ResponseCompression, True)
+ self.assertEquals(config.ResponseCompression, False)
config.load(self.testConfig)
- self.assertEquals(config.ResponseCompression, False)
+ self.assertEquals(config.ResponseCompression, True)
def testScoping(self):
- self.assertEquals(config.ResponseCompression, True)
+ self.assertEquals(config.ResponseCompression, False)
config.load(self.testConfig)
- self.assertEquals(config.ResponseCompression, False)
+ self.assertEquals(config.ResponseCompression, True)
_testResponseCompression(self)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,7 +16,7 @@
import twistedcaldav.test.util
from twisted.trial.unittest import SkipTest
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twistedcaldav.dateops import parseSQLTimestampToPyCalendar, \
parseSQLDateToPyCalendar, pyCalendarTodatetime, \
@@ -24,7 +24,7 @@
import datetime
import dateutil
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.timezone import Timezone
from twistedcaldav.timezones import TimezoneCache
class Dateops(twistedcaldav.test.util.TestCase):
@@ -43,10 +43,10 @@
"""
data = (
- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1, 0, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1, 0, 0, 0)),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0)),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
)
for value, result in data:
@@ -59,10 +59,10 @@
"""
data = (
- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
)
for value, result in data:
@@ -75,10 +75,10 @@
"""
data = (
- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1)),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1)),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0)),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
)
for value, result in data:
@@ -107,132 +107,132 @@
# Timed
(
"Start within, end within - overlap",
- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"Start before, end before - no overlap",
- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"Start before, end right before - no overlap",
- PyCalendarDateTime(2012, 1, 1, 23, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 23, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"Start before, end within - overlap",
- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"Start after, end after - no overlap",
- PyCalendarDateTime(2012, 1, 2, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 2, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"Start right after, end after - no overlap",
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"Start within, end after - overlap",
- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"Start before, end after - overlap",
- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
# All day
(
"All day: Start within, end within - overlap",
- PyCalendarDateTime(2012, 1, 9),
- PyCalendarDateTime(2012, 1, 10),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 9),
+ DateTime(2012, 1, 10),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"All day: Start before, end before - no overlap",
- PyCalendarDateTime(2012, 1, 1),
- PyCalendarDateTime(2012, 1, 2),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 1),
+ DateTime(2012, 1, 2),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"All day: Start before, end right before - no overlap",
- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 8),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 8),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"All day: Start before, end within - overlap",
- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 9),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 9),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"All day: Start after, end after - no overlap",
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 17),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 17),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"All day: Start right after, end after - no overlap",
- PyCalendarDateTime(2012, 1, 15),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 15),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
False,
),
(
"All day: Start within, end after - overlap",
- PyCalendarDateTime(2012, 1, 14),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 14),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
(
"All day: Start before, end after - overlap",
- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
True,
),
)
@@ -254,8 +254,8 @@
dateops.pyCalendarTodatetime
"""
tests = (
- (PyCalendarDateTime(2012, 4, 4, 12, 34, 56), datetime.datetime(2012, 4, 4, 12, 34, 56, tzinfo=dateutil.tz.tzutc())),
- (PyCalendarDateTime(2012, 12, 31), datetime.date(2012, 12, 31)),
+ (DateTime(2012, 4, 4, 12, 34, 56), datetime.datetime(2012, 4, 4, 12, 34, 56, tzinfo=dateutil.tz.tzutc())),
+ (DateTime(2012, 12, 31), datetime.date(2012, 12, 31)),
)
for pycal, result in tests:
@@ -267,8 +267,8 @@
dateops.parseSQLTimestampToPyCalendar
"""
tests = (
- ("2012-04-04 12:34:56", PyCalendarDateTime(2012, 4, 4, 12, 34, 56)),
- ("2012-12-31 01:01:01", PyCalendarDateTime(2012, 12, 31, 1, 1, 1)),
+ ("2012-04-04 12:34:56", DateTime(2012, 4, 4, 12, 34, 56)),
+ ("2012-12-31 01:01:01", DateTime(2012, 12, 31, 1, 1, 1)),
)
for sqlStr, result in tests:
@@ -281,8 +281,8 @@
"""
tests = (
- ("2012-04-04", PyCalendarDateTime(2012, 4, 4)),
- ("2012-12-31 00:00:00", PyCalendarDateTime(2012, 12, 31)),
+ ("2012-04-04", DateTime(2012, 4, 4)),
+ ("2012-12-31 00:00:00", DateTime(2012, 12, 31)),
)
for sqlStr, result in tests:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,16 +21,16 @@
from twisted.trial.unittest import SkipTest
from twistedcaldav.ical import Component, Property, InvalidICalendarDataError, \
- normalizeCUAddress
+ normalizeCUAddress, normalize_iCalStr
from twistedcaldav.instance import InvalidOverriddenInstanceError
import twistedcaldav.test.util
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twistedcaldav.ical import iCalendarProductID
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.duration import Duration
from twistedcaldav.dateops import normalizeForExpand
-from pycalendar.value import PyCalendarValue
+from pycalendar.value import Value
class iCalendar (twistedcaldav.test.util.TestCase):
"""
@@ -469,7 +469,7 @@
calendar.validCalendarData(doFix=False, validateRecurrences=True)
# Verify expansion works, even for an RDATE prior to master DTSTART:
- calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ calendar.expandTimeRanges(DateTime(2100, 1, 1))
# Test EXDATEs *prior* to master (as the result of client splitting a
# a recurring event and copying *all* EXDATEs to new event):
@@ -568,13 +568,13 @@
year = 2004
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, PyCalendarDateTime(year, 7, 4))
- self.assertEqual(end , PyCalendarDateTime(year, 7, 5))
+ self.assertEqual(start, DateTime(year, 7, 4))
+ self.assertEqual(end , DateTime(year, 7, 5))
if year == 2050:
break
year += 1
@@ -594,14 +594,14 @@
}
year = 2004
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
if year in results:
- self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
- self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
+ self.assertEqual(start, DateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , DateTime(year, results[year][0], results[year][2]))
if year == 2050:
break
year += 1
@@ -621,14 +621,14 @@
}
year = 2002
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
if year in results:
- self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
- self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
+ self.assertEqual(start, DateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , DateTime(year, results[year][0], results[year][2]))
if year == 2050:
break
year += 1
@@ -642,13 +642,13 @@
"""
calendar = Component.fromStream(file(os.path.join(self.data_dir, "Holidays", "C318ABFE-1ED0-11D9-A5E0-000A958A3252.ics")))
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, PyCalendarDateTime(2004, 11, 25))
- self.assertEqual(end, PyCalendarDateTime(2004, 11, 27))
+ self.assertEqual(start, DateTime(2004, 11, 25))
+ self.assertEqual(end, DateTime(2004, 11, 27))
break
# test_component_timerange.todo = "recurrence expansion should give us no end date here"
@@ -658,44 +658,44 @@
"""
parse_date()
"""
- self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
+ self.assertEqual(DateTime.parseText("19970714"), DateTime(1997, 7, 14))
def test_parse_datetime(self):
"""
parse_datetime()
"""
- dt = PyCalendarDateTime.parseText("19980118T230000")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
+ dt = DateTime.parseText("19980118T230000")
+ self.assertEqual(dt, DateTime(1998, 1, 18, 23, 0, 0))
self.assertTrue(dt.floating())
- dt = PyCalendarDateTime.parseText("19980119T070000Z")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ dt = DateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, DateTime(1998, 1, 19, 7, 0, 0, tzid=Timezone(utc=True)))
def test_parse_date_or_datetime(self):
"""
parse_date_or_datetime()
"""
- self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
+ self.assertEqual(DateTime.parseText("19970714"), DateTime(1997, 7, 14))
- dt = PyCalendarDateTime.parseText("19980118T230000")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
+ dt = DateTime.parseText("19980118T230000")
+ self.assertEqual(dt, DateTime(1998, 1, 18, 23, 0, 0))
self.assertTrue(dt.floating())
- dt = PyCalendarDateTime.parseText("19980119T070000Z")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ dt = DateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, DateTime(1998, 1, 19, 7, 0, 0, tzid=Timezone(utc=True)))
def test_parse_duration(self):
"""
parse_duration()
"""
- self.assertEqual(PyCalendarDuration.parseText("P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
- self.assertEqual(PyCalendarDuration.parseText("+P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
- self.assertEqual(PyCalendarDuration.parseText("-P15DT5H0M20S"), PyCalendarDuration(days=15 * -1, hours=5 * -1, minutes=0, seconds=20 * -1))
+ self.assertEqual(Duration.parseText("P15DT5H0M20S"), Duration(days=15, hours=5, minutes=0, seconds=20))
+ self.assertEqual(Duration.parseText("+P15DT5H0M20S"), Duration(days=15, hours=5, minutes=0, seconds=20))
+ self.assertEqual(Duration.parseText("-P15DT5H0M20S"), Duration(days=15 * -1, hours=5 * -1, minutes=0, seconds=20 * -1))
- self.assertEqual(PyCalendarDuration.parseText("P7W"), PyCalendarDuration(weeks=7))
+ self.assertEqual(Duration.parseText("P7W"), Duration(weeks=7))
def test_correct_attendee_properties(self):
@@ -807,7 +807,7 @@
""",
(
("mailto:user1 at example.com", None),
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ("mailto:user1 at example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
)
),
(
@@ -833,7 +833,7 @@
""",
(
("mailto:user1 at example.com", None),
- ("mailto:user3 at example.com", PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ("mailto:user3 at example.com", DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
)
),
(
@@ -946,8 +946,8 @@
False,
(
("mailto:user2 at example.com", None),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ("mailto:user2 at example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
)
),
(
@@ -1152,6 +1152,82 @@
self.assertEqual(result, str(component).replace("\r", ""))
+ def test_parameter_multi_values(self):
+ caldata = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;MEMBER="urn:uuid:group01","urn:uuid:group02";PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;MEMBER="urn:uuid:group01","urn:uuid:group02","urn:uuid:group03";PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata3 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;MEMBER="urn:uuid:group01";PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(caldata)
+ attendee = component.masterComponent().getAttendeeProperty(["mailto:user02 at example.com", ])
+ self.assertTrue(attendee is not None)
+
+ # Single value retrieved as multi-value
+ partstat = attendee.parameterValues("PARTSTAT")
+ self.assertEqual(partstat, ["NEEDS-ACTION"])
+
+ # Multi-value retrieved as single-value
+ member = attendee.parameterValue("MEMBER")
+ self.assertEqual(member, "urn:uuid:group01")
+
+ # Multi-value retrieved as multi-value
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02"])
+
+ # Multi-value add a new value
+ members = attendee.parameterValues("MEMBER")
+ members.append("urn:uuid:group03")
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02", "urn:uuid:group03"])
+ self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata2))
+
+ # Multi-value back to one
+ members = attendee.parameterValues("MEMBER")
+ del members[1:]
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01"])
+ self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata3))
+
+
def test_add_property_with_valuetype(self):
data = """BEGIN:VCALENDAR
VERSION:2.0
@@ -1177,7 +1253,7 @@
component = Component.fromString(data)
vevent = component.mainComponent()
- vevent.addProperty(Property("ATTACH", "foobar", valuetype=PyCalendarValue.VALUETYPE_BINARY))
+ vevent.addProperty(Property("ATTACH", "foobar", valuetype=Value.VALUETYPE_BINARY))
self.assertEqual(str(component), result)
@@ -2277,8 +2353,8 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2299,12 +2375,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2326,16 +2402,16 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 2, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2357,12 +2433,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2384,12 +2460,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2417,12 +2493,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2474,12 +2550,12 @@
True,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2488,9 +2564,9 @@
for description, original, ignoreInvalidInstances, results in data:
component = Component.fromString(original)
if results is None:
- self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
+ self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
else:
- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
self.assertEqual(periods, results)
@@ -2518,8 +2594,8 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2539,8 +2615,8 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
),
)
),
@@ -2561,12 +2637,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2587,12 +2663,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
),
(
- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 17),
+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 17),
),
)
),
@@ -2614,16 +2690,16 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 2, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2645,16 +2721,16 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
),
(
- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 17),
+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 17),
),
(
- PyCalendarDateTime(2007, 11, 18),
- PyCalendarDateTime(2007, 11, 19),
+ DateTime(2007, 11, 18),
+ DateTime(2007, 11, 19),
),
)
),
@@ -2676,12 +2752,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2703,12 +2779,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
),
(
- PyCalendarDateTime(2007, 11, 17),
- PyCalendarDateTime(2007, 11, 18),
+ DateTime(2007, 11, 17),
+ DateTime(2007, 11, 18),
),
)
),
@@ -2736,12 +2812,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -2769,12 +2845,12 @@
False,
(
(
- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
),
(
- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 18),
+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 18),
),
)
),
@@ -2831,9 +2907,9 @@
for description, original, ignoreInvalidInstances, results in data:
component = Component.fromString(original)
if results is None:
- self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
+ self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
else:
- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
self.assertEqual(periods, results)
@@ -2861,8 +2937,8 @@
None,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -2880,11 +2956,11 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2007, 1, 1),
+ DateTime(2007, 1, 1),
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -2902,9 +2978,9 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
(),
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
),
(
"Simple recurring - no limit",
@@ -2923,20 +2999,20 @@
None,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -2955,23 +3031,23 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2007, 1, 1),
+ DateTime(2007, 1, 1),
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -2990,14 +3066,14 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
(
(
- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
),
(
"Simple recurring - limit effective full",
@@ -3013,9 +3089,9 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2012, 1, 1),
+ DateTime(2012, 1, 1),
(),
- PyCalendarDateTime(2012, 1, 1),
+ DateTime(2012, 1, 1),
),
(
"Complex recurring - no limit",
@@ -3048,20 +3124,20 @@
None,
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -3094,23 +3170,23 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2007, 1, 1),
+ DateTime(2007, 1, 1),
(
(
- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
None,
@@ -3143,14 +3219,14 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
(
(
- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
),
),
- PyCalendarDateTime(2010, 1, 1),
+ DateTime(2010, 1, 1),
),
(
"Complex recurring - limit effective full",
@@ -3180,15 +3256,15 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2012, 1, 1),
+ DateTime(2012, 1, 1),
(),
- PyCalendarDateTime(2012, 1, 1),
+ DateTime(2012, 1, 1),
),
)
for description, original, lowerLimit, results, limited in data:
component = Component.fromString(original)
- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), lowerLimit=lowerLimit)
+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), lowerLimit=lowerLimit)
self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
self.assertEqual(periods, results)
@@ -4040,7 +4116,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T080000Z
@@ -4065,7 +4141,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 18, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T180000Z
@@ -4091,7 +4167,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 3, 18, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090103T180000Z
@@ -4115,7 +4191,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 9, 0, 0, tzid=Timezone(utc=True)),
None,
),
(
@@ -4133,7 +4209,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 19, 0, 0, tzid=Timezone(utc=True)),
None,
),
(
@@ -4152,7 +4228,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 3, 19, 0, 0, tzid=Timezone(utc=True)),
None,
),
(
@@ -4169,7 +4245,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 8),
+ DateTime(2009, 1, 8),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090108
@@ -4194,7 +4270,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 3),
+ DateTime(2009, 1, 3),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090103
@@ -4220,7 +4296,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 10),
+ DateTime(2009, 1, 10),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090110
@@ -4244,7 +4320,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 3),
+ DateTime(2009, 1, 3),
None,
),
(
@@ -4262,7 +4338,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 5),
+ DateTime(2009, 1, 5),
None,
),
(
@@ -4281,7 +4357,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 19),
+ DateTime(2009, 1, 19),
None,
),
)
@@ -4311,8 +4387,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 4, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -4349,8 +4425,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 18, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 4, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -4388,8 +4464,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 5, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 3, 18, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 5, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -4425,8 +4501,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 9, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
None,
@@ -4456,8 +4532,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 19, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
None,
@@ -4488,8 +4564,8 @@
END:VCALENDAR
""",
(
- PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 3, 19, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
),
(
None,
@@ -4534,7 +4610,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T080000Z
@@ -4561,7 +4637,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T080000Z
@@ -4604,19 +4680,19 @@
self.assertFalse(hasattr(ical, "cachedInstances"))
# Derive one day apart - no re-cache
- ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ical.deriveInstance(DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)))
self.assertTrue(hasattr(ical, "cachedInstances"))
oldLimit = ical.cachedInstances.limit
- ical.deriveInstance(PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ical.deriveInstance(DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)))
self.assertEqual(ical.cachedInstances.limit, oldLimit)
# Derive several years ahead - re-cached
- ical.deriveInstance(PyCalendarDateTime(2011, 1, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ical.deriveInstance(DateTime(2011, 1, 1, 8, 0, 0, tzid=Timezone(utc=True)))
self.assertNotEqual(ical.cachedInstances.limit, oldLimit)
oldLimit = ical.cachedInstances.limit
# Check one day ahead again - no re-cache
- ical.deriveInstance(PyCalendarDateTime(2011, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+ ical.deriveInstance(DateTime(2011, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)))
self.assertEqual(ical.cachedInstances.limit, oldLimit)
@@ -4661,13 +4737,13 @@
masterDerived = ical.masterDerived()
# Derive one day apart - no re-cache
- result = ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
+ result = ical.deriveInstance(DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
self.assertEqual(str(result), derived1)
- result = ical.deriveInstance(PyCalendarDateTime(2009, 2, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
+ result = ical.deriveInstance(DateTime(2009, 2, 3, 8, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
self.assertEqual(str(result), derived2)
- result = ical.deriveInstance(PyCalendarDateTime(2009, 3, 3, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
+ result = ical.deriveInstance(DateTime(2009, 3, 3, 9, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
self.assertEqual(result, None)
self.assertEqual(str(ical), event)
@@ -4847,8 +4923,8 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4866,9 +4942,9 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 5, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 5, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4886,10 +4962,10 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4908,11 +4984,11 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 2, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4932,12 +5008,12 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 2, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 3, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4960,11 +5036,11 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -4989,12 +5065,12 @@
""",
(
(None, True),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -5012,9 +5088,9 @@
""",
(
(None, False),
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -5046,7 +5122,7 @@
END:VCALENDAR
""",
(
- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
)
),
(
@@ -5062,7 +5138,7 @@
END:VCALENDAR
""",
(
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
(
@@ -5084,7 +5160,7 @@
END:VCALENDAR
""",
(
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), False),
)
),
)
@@ -5396,12 +5472,12 @@
""",
(
(
- PyCalendarDateTime(2007, 11, 14, 20, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 14, 20, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 21, 0, 0, tzid=Timezone(utc=True)),
),
(
- PyCalendarDateTime(2007, 11, 15, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 22, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2007, 11, 15, 21, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 22, 0, 0, tzid=Timezone(utc=True)),
),
)
),
@@ -5409,7 +5485,7 @@
for description, original, fixed, results in data:
component = Component.fromString(original)
- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=False)
+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=False)
self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
self.assertEqual(periods, results)
@@ -5696,7 +5772,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
),
@@ -5741,7 +5817,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -5795,7 +5871,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -5841,19 +5917,19 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
),
),
(
- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", True,),
),
),
(
- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", True,),
),
@@ -5916,21 +5992,21 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", True,),
),
),
(
- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", True,),
),
),
(
- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -6015,7 +6091,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", True,),
@@ -6023,7 +6099,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", True,),
@@ -6031,7 +6107,7 @@
),
),
(
- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -7415,7 +7491,7 @@
""",
),
)
- cutoff = PyCalendarDateTime(2011, 11, 30, 0, 0, 0)
+ cutoff = DateTime(2011, 11, 30, 0, 0, 0)
for _ignore_title, expected, body in data:
ical = Component.fromString(body)
self.assertEquals(expected, ical.hasInstancesAfter(cutoff))
@@ -9437,7 +9513,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9505,7 +9581,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9577,7 +9653,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9626,7 +9702,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9676,7 +9752,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 31, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 31, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9725,7 +9801,7 @@
END:VEVENT
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 1, 31, 6, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 1, 31, 6, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9795,7 +9871,7 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -9944,7 +10020,7 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,7 +20,8 @@
from twistedcaldav.ical import Component
from twistedcaldav.test.util import TestCase
from twistedcaldav.config import ConfigDict
-from pycalendar.datetime import PyCalendarDateTime
+from twistedcaldav.timezones import TimezoneCache
+from pycalendar.datetime import DateTime
import os
@@ -52,6 +53,11 @@
class LocalizationTests(TestCase):
+ def setUp(self):
+ super(LocalizationTests, self).setUp()
+ TimezoneCache.create()
+
+
def test_BasicStringLocalization(self):
with translationTo('pig', localeDir=localeDir):
@@ -68,22 +74,22 @@
with translationTo('en', localeDir=localeDir) as t:
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
def test_TimeFormatting24Hour(self):
with translationTo('pig', localeDir=localeDir) as t:
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "00:00")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "23:59")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "06:05")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "16:05")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "00:00")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "23:59")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "06:05")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "16:05")
def test_CalendarFormatting(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,6 +16,7 @@
from twext.python.filepath import CachingFilePath as FilePath
from twext.web2 import responsecode
from twext.web2.dav.util import davXMLFromStream, joinURL
+from twext.web2.http_headers import Headers, MimeType
from twext.web2.iweb import IResponse
from twext.web2.stream import MemoryStream
@@ -268,7 +269,13 @@
if data:
for filename, icaldata in data.iteritems():
- request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, filename + ".ics"), authid="wsanchez")
+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(calendar_uri, filename + ".ics"),
+ headers=Headers({"content-type": MimeType.fromString("text/calendar")}),
+ authid="wsanchez"
+ )
request.stream = MemoryStream(icaldata)
yield self.send(request)
else:
@@ -276,7 +283,13 @@
for child in FilePath(self.holidays_dir).children():
if os.path.splitext(child.basename())[1] != ".ics":
continue
- request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, child.basename()), authid="wsanchez")
+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(calendar_uri, child.basename()),
+ headers=Headers({"content-type": MimeType.fromString("text/calendar")}),
+ authid="wsanchez"
+ )
request.stream = MemoryStream(child.getContent())
yield self.send(request)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -88,7 +88,7 @@
self.fail("Expected CalDAV:supported-calendar-data element; but got none.")
for calendar in supported_calendar.children:
- if calendar.content_type != "text/calendar":
+ if calendar.content_type not in ("text/calendar", "application/calendar+json"):
self.fail("Expected a text/calendar calendar-data type restriction")
if calendar.version != "2.0":
self.fail("Expected a version 2.0 calendar-data restriction")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -19,8 +19,8 @@
from twistedcaldav.ical import Component
from twistedcaldav.timezones import TimezoneCache, TimezoneException
from twistedcaldav.timezones import readTZ, listTZs
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
import os
import threading
@@ -49,7 +49,7 @@
if calendar.name() != "VCALENDAR":
self.fail("Calendar is not a VCALENDAR")
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
@@ -69,8 +69,8 @@
self.doTest(
"TruncatedApr01.ics",
- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True))
)
@@ -84,8 +84,8 @@
self.doTest(
"TruncatedDec10.ics",
- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True))
)
@@ -99,13 +99,13 @@
self.doTest(
"TruncatedApr01.ics",
- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True)),
)
self.doTest(
"TruncatedDec10.ics",
- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True)),
testEqual=False
)
@@ -119,13 +119,13 @@
self.doTest(
"TruncatedApr01.ics",
- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True)),
)
self.doTest(
"TruncatedDec10.ics",
- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True)),
)
@@ -139,13 +139,13 @@
self.doTest(
"TruncatedDec10.ics",
- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True))
)
self.doTest(
"TruncatedApr01.ics",
- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True))
)
@@ -201,13 +201,13 @@
calendar = Component.fromString(data)
if calendar.name() != "VCALENDAR":
self.fail("Calendar is not a VCALENDAR")
- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, PyCalendarDateTime(2007, 12, 25, 05, 0, 0, PyCalendarTimezone(utc=True)))
- self.assertEqual(end, PyCalendarDateTime(2007, 12, 25, 06, 0, 0, PyCalendarTimezone(utc=True)))
+ self.assertEqual(start, DateTime(2007, 12, 25, 05, 0, 0, Timezone(utc=True)))
+ self.assertEqual(end, DateTime(2007, 12, 25, 06, 0, 0, Timezone(utc=True)))
break
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1675,8 +1675,8 @@
ATTENDEE;CN=Double 'quotey' Quotes;CUTYPE=INDIVIDUAL;EMAIL=doublequotes at ex
ample.com;PARTSTAT=ACCEPTED:urn:uuid:8E04787E-336D-41ED-A70B-D233AD0DCE6F
ATTENDEE;CN=Cyrus Daboo;CUTYPE=INDIVIDUAL;EMAIL=cdaboo at example.com;PARTSTA
- T=ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89
- 500
+ T=ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A8
+ 9500
CREATED:20090203T181910Z
DESCRIPTION:This has " Bad Quotes " in it
DTSTAMP:20090203T181924Z
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py (from rev 12016, CalendarServer/trunk/twistedcaldav/test/test_util.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,144 @@
+##
+# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twext.web2.http_headers import Headers
+
+import twistedcaldav.test.util
+from twistedcaldav.util import bestAcceptType
+
+class AcceptType(twistedcaldav.test.util.TestCase):
+ """
+ L{bestAcceptType} tests
+ """
+ def test_bestAcceptType(self):
+
+ data = (
+ (
+ "#1.1",
+ ("Accept", "text/plain"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#1.2",
+ ("Accept", "text/plain"),
+ ["text/calendar"],
+ None,
+ ),
+ (
+ "#1.3",
+ ("Accept", "text/*"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#1.4",
+ ("Accept", "*/*"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#2.1",
+ ("Accept", "text/plain"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.2",
+ ("Accept", "text/plain"),
+ ["text/calendar", "application/text", ],
+ None,
+ ),
+ (
+ "#2.3",
+ ("Accept", "text/*"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.4",
+ ("Accept", "*/*"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.5",
+ ("Accept", "application/text"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#2.6",
+ ("Accept", "application/*"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#3.1",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.2",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/calendar", "application/calendar", ],
+ None,
+ ),
+ (
+ "#3.3",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.4",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.5",
+ ("Accept", "text/plain;q=0.3, application/text;q=0.5"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#3.6",
+ ("Accept", "text/plain;q=0.5, application/*;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#4.1",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.2, text/*;q=0.3"),
+ ["text/calendar", "application/text", ],
+ "text/calendar",
+ ),
+ (
+ "#5.1",
+ None,
+ ["text/calendar", "application/text", ],
+ "text/calendar",
+ ),
+ )
+
+ for title, hdr, allowedTypes, result in data:
+ hdrs = Headers()
+ if hdr:
+ hdrs.addRawHeader(*hdr)
+ check = bestAcceptType(hdrs.getHeader("accept"), allowedTypes)
+ self.assertEqual(check, result, msg="Failed %s" % (title,))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -22,7 +22,7 @@
from twistedcaldav.config import config
-from pycalendar.timezonedb import PyCalendarTimezoneDatabase
+from pycalendar.timezonedb import TimezoneDatabase
log = Logger()
@@ -95,7 +95,7 @@
TimezoneCache.dirName = None
TimezoneCache.validatePath()
TimezoneCache.version = TimezoneCache.getTZVersion(TimezoneCache.getDBPath())
- PyCalendarTimezoneDatabase.createTimezoneDatabase(TimezoneCache.getDBPath())
+ TimezoneDatabase.createTimezoneDatabase(TimezoneCache.getDBPath())
@staticmethod
@@ -171,7 +171,7 @@
@staticmethod
def clear():
- PyCalendarTimezoneDatabase.clearTimezoneDatabase()
+ TimezoneDatabase.clearTimezoneDatabase()
# zoneinfo never changes in a running instance so cache all this data as we use it
cachedTZs = {}
@@ -207,7 +207,7 @@
if tzid not in cachedVTZs:
- tzcal = PyCalendarTimezoneDatabase.getTimezoneInCalendar(tzid)
+ tzcal = TimezoneDatabase.getTimezoneInCalendar(tzid)
if tzcal:
cachedVTZs[tzid] = tzcal
else:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -46,7 +46,7 @@
from twistedcaldav.timezones import listTZs
from twistedcaldav.timezones import readTZ
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
class TimezoneServiceResource (ReadOnlyNoCopyResourceMixIn, DAVResourceWithoutChildrenMixin, DAVResource):
"""
@@ -66,20 +66,25 @@
self.parent = parent
self.cache = {}
+
def deadProperties(self):
if not hasattr(self, "_dead_properties"):
self._dead_properties = NonePropertyStore(self)
return self._dead_properties
+
def etag(self):
return succeed(None)
+
def checkPreconditions(self, request):
return None
+
def checkPrivileges(self, request, privileges, recurse=False, principal=None, inherited_aces=None):
return succeed(None)
+
def defaultAccessControlList(self):
return davxml.ACL(
# DAV:Read for all principals (includes anonymous)
@@ -92,21 +97,27 @@
),
)
+
def contentType(self):
return MimeType.fromString("text/xml")
+
def resourceType(self):
return davxml.ResourceType.timezones
+
def isCollection(self):
return False
+
def isCalendarCollection(self):
return False
+
def isPseudoCalendarCollection(self):
return False
+
def render(self, request):
output = """<html>
<head>
@@ -127,10 +138,11 @@
"""
The timezone service POST method.
"""
-
+
# GET and POST do the same thing
return self.http_POST(request)
+
def http_POST(self, request):
"""
The timezone service POST method.
@@ -138,11 +150,11 @@
# Check authentication and access controls
def _gotResult(_):
-
+
if not request.args:
# Do normal GET behavior
return self.render(request)
-
+
method = request.args.get("method", ("",))
if len(method) != 1:
raise HTTPError(ErrorResponse(
@@ -151,41 +163,43 @@
"Invalid method query parameter",
))
method = method[0]
-
+
action = {
"list" : self.doPOSTList,
"get" : self.doPOSTGet,
"expand" : self.doPOSTExpand,
}.get(method, None)
-
+
if action is None:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
(calendarserver_namespace, "supported-method"),
"Unknown method query parameter",
))
-
+
return action(request)
-
+
d = self.authorize(request, (davxml.Read(),))
d.addCallback(_gotResult)
return d
+
def doPOSTList(self, request):
"""
Return a list of all timezones known to the server.
"""
-
+
tzids = listTZs()
tzids.sort()
result = customxml.TZIDs(*[customxml.TZID(tzid) for tzid in tzids])
return XMLResponse(responsecode.OK, result)
+
def doPOSTGet(self, request):
"""
Return the specified timezone data.
"""
-
+
tzid = request.args.get("tzid", ())
if len(tzid) != 1:
raise HTTPError(ErrorResponse(
@@ -209,6 +223,7 @@
response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
return response
+
def doPOSTExpand(self, request):
"""
Expand a timezone within specified start/end dates.
@@ -235,7 +250,7 @@
start = request.args.get("start", ())
if len(start) != 1:
raise ValueError()
- start = PyCalendarDateTime.parseText(start[0])
+ start = DateTime.parseText(start[0])
except ValueError:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
@@ -247,7 +262,7 @@
end = request.args.get("end", ())
if len(end) != 1:
raise ValueError()
- end = PyCalendarDateTime.parseText(end[0])
+ end = DateTime.parseText(end[0])
if end <= start:
raise ValueError()
except ValueError:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -49,9 +49,9 @@
addVTZ
from twistedcaldav.xmlutil import addSubElement
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarInvalidData
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData
import hashlib
import itertools
@@ -89,7 +89,13 @@
else:
raise ValueError("Invalid TimezoneService mode: %s" % (config.TimezoneService.Mode,))
+ self.formats = []
+ self.formats.append("text/calendar")
+ self.formats.append("text/plain")
+ if config.EnableJSONData:
+ self.formats.append("application/calendar+json")
+
def _initPrimaryService(self):
tzpath = TimezoneCache.getDBPath()
xmlfile = os.path.join(tzpath, "timezones.xml")
@@ -268,7 +274,7 @@
{
"name": "get",
"parameters": [
- {"name": "format", "required": False, "multi": False, "values": ["text/calendar", "text/plain", ], },
+ {"name": "format", "required": False, "multi": False, "values": self.formats, },
{"name": "tzid", "required": True, "multi": False, },
],
},
@@ -303,7 +309,7 @@
# Validate a date-time stamp
changedsince = changedsince[0]
try:
- dt = PyCalendarDateTime.parseText(changedsince)
+ dt = DateTime.parseText(changedsince)
except ValueError:
raise HTTPError(JSONResponse(
responsecode.BAD_REQUEST,
@@ -348,7 +354,7 @@
))
format = request.args.get("format", ("text/calendar",))
- if len(format) != 1 or format[0] not in ("text/calendar", "text/plain",):
+ if len(format) != 1 or format[0] not in self.formats:
raise HTTPError(JSONResponse(
responsecode.BAD_REQUEST,
{
@@ -368,7 +374,7 @@
}
))
- tzdata = calendar.getText()
+ tzdata = calendar.getText(format=format if format != "text/plain" else None)
response = Response()
response.stream = MemoryStream(tzdata)
@@ -396,9 +402,9 @@
if len(start) > 1:
raise ValueError()
elif len(start) == 1:
- start = PyCalendarDateTime.parseText(start[0])
+ start = DateTime.parseText(start[0])
else:
- start = PyCalendarDateTime.getToday()
+ start = DateTime.getToday()
start.setDay(1)
start.setMonth(1)
except ValueError:
@@ -415,9 +421,9 @@
if len(end) > 1:
raise ValueError()
elif len(end) == 1:
- end = PyCalendarDateTime.parseText(end[0])
+ end = DateTime.parseText(end[0])
else:
- end = PyCalendarDateTime.getToday()
+ end = DateTime.getToday()
end.setDay(1)
end.setMonth(1)
end.offsetYear(10)
@@ -558,7 +564,7 @@
Generate a PyCalendar containing the requested timezone.
"""
# We will just use our existing TimezoneCache here
- calendar = PyCalendar()
+ calendar = Calendar()
try:
vtz = readVTZ(tzid)
calendar.addComponent(vtz.getComponents()[0].duplicate())
@@ -618,7 +624,7 @@
Create a new DB xml file from scratch by scanning zoneinfo.
"""
- self.dtstamp = PyCalendarDateTime.getNowUTC().getXMLText()
+ self.dtstamp = DateTime.getNowUTC().getXMLText()
self._scanTZs("")
self._dumpTZs()
@@ -672,7 +678,7 @@
"""
Update existing DB info by comparing md5's.
"""
- self.dtstamp = PyCalendarDateTime.getNowUTC().getXMLText()
+ self.dtstamp = DateTime.getNowUTC().getXMLText()
self.changeCount = 0
self.changed = set()
self._scanTZs("", checkIfChanged=True)
@@ -847,8 +853,8 @@
ical = response.data
try:
- calendar = PyCalendar.parseText(ical)
- except PyCalendarInvalidData:
+ calendar = Calendar.parseText(ical)
+ except InvalidData:
log.error("Invalid calendar data for tzid: %s" % (tzinfo.tzid,))
returnValue(None)
ical = calendar.getText()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1065,7 +1065,6 @@
# Migrate mail tokens from sqlite to store
yield migrateTokensToStore(self.config.DataRoot, self.store)
-
@inlineCallbacks
def processInboxItems(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -517,3 +517,52 @@
cuas = principal.record.calendarUserAddresses
return (fullName, rec.guid, cuas)
+
+
+
+def bestAcceptType(accepts, allowedTypes):
+ """
+ Given a set of Accept headers and the set of types the server can return, determine the best choice
+ of format to return to the client.
+
+ @param accepts: parsed accept headers
+ @type accepts: C{dict}
+ @param allowedTypes: list of allowed types in server preferred order
+ @type allowedTypes: C{list}
+ """
+
+ # If no Accept present just use the first allowed type - the server's preference
+ if not accepts:
+ return allowedTypes[0]
+
+ # Get mapping for ordered top-level types for use in subtype wildcard match
+ toptypes = {}
+ for allowed in allowedTypes:
+ mediaType = allowed.split("/")[0]
+ if mediaType not in toptypes:
+ toptypes[mediaType] = allowed
+
+ result = None
+ result_qval = 0.0
+ for content_type, qval in accepts.items():
+ # Exact match
+ ctype = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ if ctype in allowedTypes:
+ if qval > result_qval:
+ result = ctype
+ result_qval = qval
+
+ # Subtype wildcard match
+ elif content_type.mediaType != "*" and content_type.mediaSubtype == "*":
+ if content_type.mediaType in toptypes:
+ if qval > result_qval:
+ result = toptypes[content_type.mediaType]
+ result_qval = qval
+
+ # Full wildcard match
+ elif content_type.mediaType == "*" and content_type.mediaSubtype == "*":
+ if qval > result_qval:
+ result = allowedTypes[0]
+ result_qval = qval
+
+ return result
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,9 +31,11 @@
from twext.web2.stream import IStream
from twext.web2.dav.util import allDataFromStream
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.exceptions import PyCalendarError
+from twistedcaldav.config import config
+
+from pycalendar.parameter import Parameter
+from pycalendar.componentbase import ComponentBase
+from pycalendar.exceptions import ErrorBase
from pycalendar.vcard.card import Card
from pycalendar.vcard.property import Property as pyProperty
@@ -44,6 +46,8 @@
class InvalidVCardDataError(ValueError):
pass
+
+
class Property (object):
"""
vCard Property
@@ -73,31 +77,55 @@
for attrname, attrvalue in params.items():
if isinstance(attrvalue, unicode):
attrvalue = attrvalue.encode("utf-8")
- self._pycard.addAttribute(PyCalendarAttribute(attrname, attrvalue))
+ self._pycard.addParameter(Parameter(attrname, attrvalue))
- def __str__ (self): return str(self._pycard)
- def __repr__(self): return "<%s: %r: %r>" % (self.__class__.__name__, self.name(), self.value())
- def __hash__(self): return hash(str(self))
+ def __str__(self):
+ return str(self._pycard)
- def __ne__(self, other): return not self.__eq__(other)
+
+ def __repr__(self):
+ return "<%s: %r: %r>" % (self.__class__.__name__, self.name(), self.value())
+
+
+ def __hash__(self):
+ return hash(str(self))
+
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
def __eq__(self, other):
- if not isinstance(other, Property): return False
+ if not isinstance(other, Property):
+ return False
return self._pycard == other._pycard
- def __gt__(self, other): return not (self.__eq__(other) or self.__lt__(other))
+
+ def __gt__(self, other):
+ return not (self.__eq__(other) or self.__lt__(other))
+
+
def __lt__(self, other):
my_name = self.name()
other_name = other.name()
- if my_name < other_name: return True
- if my_name > other_name: return False
+ if my_name < other_name:
+ return True
+ if my_name > other_name:
+ return False
return self.value() < other.value()
- def __ge__(self, other): return self.__eq__(other) or self.__gt__(other)
- def __le__(self, other): return self.__eq__(other) or self.__lt__(other)
+ def __ge__(self, other):
+ return self.__eq__(other) or self.__gt__(other)
+
+
+ def __le__(self, other):
+ return self.__eq__(other) or self.__lt__(other)
+
+
def duplicate(self):
"""
Duplicate this object and all its contents.
@@ -105,35 +133,45 @@
"""
return Property(None, None, params=None, pycard=self._pycard.duplicate())
- def name (self): return self._pycard.getName()
- def value (self): return self._pycard.getValue().getValue()
+ def name(self):
+ return self._pycard.getName()
- def strvalue (self): return str(self._pycard.getValue())
+ def value(self):
+ return self._pycard.getValue().getValue()
+
+
+ def strvalue(self):
+ return str(self._pycard.getValue())
+
+
def setValue(self, value):
self._pycard.setValue(value)
+
def parameterNames(self):
"""
Returns a set containing parameter names for this property.
"""
result = set()
- for pyattrlist in self._pycard.getAttributes().values():
+ for pyattrlist in self._pycard.getParameters().values():
for pyattr in pyattrlist:
result.add(pyattr.getName())
return result
+
def parameterValue(self, name, default=None):
"""
Returns a single value for the given parameter. Raises
InvalidICalendarDataError if the parameter has more than one value.
"""
try:
- return self._pycard.getAttributeValue(name)
+ return self._pycard.getParameterValue(name)
except KeyError:
return default
+
def parameterValues(self, name):
"""
Returns a single value for the given parameter. Raises
@@ -141,7 +179,7 @@
"""
results = []
try:
- attrs = self._pycard.getAttributes()[name.upper()]
+ attrs = self._pycard.getParameters()[name.upper()]
except KeyError:
return []
@@ -149,28 +187,33 @@
results.extend(attr.getValues())
return results
+
def hasParameter(self, paramname):
- return self._pycard.hasAttribute(paramname)
+ return self._pycard.hasParameter(paramname)
+
def setParameter(self, paramname, paramvalue):
- self._pycard.replaceAttribute(PyCalendarAttribute(paramname, paramvalue))
+ self._pycard.replaceParameter(Parameter(paramname, paramvalue))
+
def removeParameter(self, paramname):
- self._pycard.removeAttributes(paramname)
+ self._pycard.removeParameters(paramname)
+
def removeAllParameters(self):
- self._pycard.setAttributes({})
+ self._pycard.setParameters({})
+
def removeParameterValue(self, paramname, paramvalue):
paramname = paramname.upper()
for attrName in self.parameterNames():
if attrName.upper() == paramname:
- for attr in tuple(self._pycard.getAttributes()[attrName]):
+ for attr in tuple(self._pycard.getParameters()[attrName]):
for value in attr.getValues():
if value == paramvalue:
if not attr.removeValue(value):
- self._pycard.removeAttributes(paramname)
+ self._pycard.removeParameters(paramname)
@@ -178,8 +221,20 @@
"""
X{vCard} component.
"""
+ allowedTypesList = None
+
+
@classmethod
- def allFromString(clazz, string):
+ def allowedTypes(cls):
+ if cls.allowedTypesList is None:
+ cls.allowedTypesList = ["text/vcard"]
+ if config.EnableJSONData:
+ cls.allowedTypesList.append("application/vcard+json")
+ return cls.allowedTypesList
+
+
+ @classmethod
+ def allFromString(clazz, string, format=None):
"""
FIXME: Just default to reading a single VCARD - actually need more
"""
@@ -193,62 +248,85 @@
if string[:3] == codecs.BOM_UTF8:
string = string[3:]
- return clazz.allFromStream(StringIO.StringIO(string))
+ return clazz.allFromStream(StringIO.StringIO(string), format)
+
@classmethod
- def allFromStream(clazz, stream):
+ def allFromStream(clazz, stream, format=None):
"""
FIXME: Just default to reading a single VCARD - actually need more
"""
try:
- results = Card.parseMultiple(stream)
- except PyCalendarError:
+ results = Card.parseMultipleData(stream, format)
+ except ErrorBase:
results = None
if not results:
stream.seek(0)
raise InvalidVCardDataError("%s" % (stream.read(),))
return [clazz(None, pycard=result) for result in results]
+
@classmethod
- def fromString(clazz, string):
+ def fromString(clazz, string, format=None):
"""
Construct a L{Component} from a string.
@param string: a string containing vCard data.
@return: a L{Component} representing the first component described by
C{string}.
"""
- if type(string) is unicode:
- string = string.encode("utf-8")
- else:
- # Valid utf-8 please
- string.decode("utf-8")
+ return clazz._fromData(string, False, format)
- # No BOMs please
- if string[:3] == codecs.BOM_UTF8:
- string = string[3:]
- return clazz.fromStream(StringIO.StringIO(string))
+ @classmethod
+ def fromStream(clazz, stream, format=None):
+ """
+ Construct a L{Component} from a stream.
+ @param stream: a C{read()}able stream containing vCard data.
+ @return: a L{Component} representing the first component described by
+ C{stream}.
+ """
+ return clazz._fromData(stream, True, format)
+
@classmethod
- def fromStream(clazz, stream):
+ def _fromData(clazz, data, isstream, format=None):
"""
Construct a L{Component} from a stream.
@param stream: a C{read()}able stream containing vCard data.
+ @param format: a C{str} indicating whether the data is vCard or jCard
@return: a L{Component} representing the first component described by
C{stream}.
"""
- cal = Card()
+
+ if isstream:
+ pass
+ else:
+ if type(data) is unicode:
+ data = data.encode("utf-8")
+ else:
+ # Valid utf-8 please
+ data.decode("utf-8")
+
+ # No BOMs please
+ if data[:3] == codecs.BOM_UTF8:
+ data = data[3:]
+
+ errmsg = "Unknown"
try:
- result = cal.parse(stream)
- except PyCalendarError:
+ result = Card.parseData(data, format)
+ except ErrorBase, e:
+ errmsg = "%s: %s" % (e.mReason, e.mData,)
result = None
if not result:
- stream.seek(0)
- raise InvalidVCardDataError("%s" % (stream.read(),))
- return clazz(None, pycard=cal)
+ if isstream:
+ data.seek(0)
+ data = data.read()
+ raise InvalidVCardDataError("%s\n%s" % (errmsg, data,))
+ return clazz(None, pycard=result)
+
@classmethod
- def fromIStream(clazz, stream):
+ def fromIStream(clazz, stream, format=None):
"""
Construct a L{Component} from a stream.
@param stream: an L{IStream} containing vCard data.
@@ -261,9 +339,11 @@
# A better solution would parse directly and incrementally from the
# request stream.
#
- def parse(data): return clazz.fromString(data)
+ def parse(data):
+ return clazz.fromString(data, format)
return allDataFromStream(IStream(stream), parse)
+
def __init__(self, name, **kwargs):
"""
Use this constructor to initialize an empty L{Component}.
@@ -277,8 +357,8 @@
pyobj = kwargs["pycard"]
if pyobj is not None:
- if not isinstance(pyobj, PyCalendarComponentBase):
- raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))
+ if not isinstance(pyobj, ComponentBase):
+ raise TypeError("Not a ComponentBase: %r" % (pyobj,))
self._pycard = pyobj
else:
@@ -302,28 +382,53 @@
else:
raise ValueError("VCards have no child components")
- def __str__ (self): return str(self._pycard)
- def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, str(self._pycard))
+ def __str__(self):
+ return str(self._pycard)
+
+
+ def __repr__(self):
+ return "<%s: %r>" % (self.__class__.__name__, str(self._pycard))
+
+
def __hash__(self):
return hash(str(self))
- def __ne__(self, other): return not self.__eq__(other)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
def __eq__(self, other):
if not isinstance(other, Component):
return False
return self._pycard == other._pycard
+
+ def getText(self, format=None):
+ """
+ Return text representation
+ """
+ assert self.name() == "VCARD", "Must be a VCARD: %r" % (self,)
+
+ result = self._pycard.getText(format)
+ if result is None:
+ raise ValueError("Unknown format requested for address data.")
+ return result
+
+
# FIXME: Should this not be in __eq__?
def same(self, other):
return self._pycard == other._pycard
+
def name(self):
"""
- @return: the name of the iCalendar type of this component.
+ @return: the name of the vCard type of this component.
"""
return self._pycard.getType()
+
def duplicate(self):
"""
Duplicate this object and all its contents.
@@ -331,6 +436,7 @@
"""
return Component(None, pycard=self._pycard.duplicate())
+
def hasProperty(self, name):
"""
@param name: the name of the property whose existence is being tested.
@@ -338,6 +444,7 @@
"""
return self._pycard.hasProperty(name)
+
def getProperty(self, name):
"""
Get one property from the property list.
@@ -346,10 +453,13 @@
@raise: L{ValueError} if there is more than one property of the given name.
"""
properties = tuple(self.properties(name))
- if len(properties) == 1: return properties[0]
- if len(properties) > 1: raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
+ if len(properties) == 1:
+ return properties[0]
+ if len(properties) > 1:
+ raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
return None
+
def properties(self, name=None):
"""
@param name: if given and not C{None}, restricts the returned properties
@@ -368,6 +478,7 @@
for p in properties
)
+
def propertyValue(self, name):
properties = tuple(self.properties(name))
if len(properties) == 1:
@@ -385,6 +496,7 @@
self._pycard.addProperty(property._pycard)
self._pycard.finalise()
+
def removeProperty(self, property):
"""
Remove a property from this component.
@@ -393,6 +505,7 @@
self._pycard.removeProperty(property._pycard)
self._pycard.finalise()
+
def removeProperties(self, name):
"""
remove all properties with name
@@ -400,6 +513,7 @@
"""
self._pycard.removeProperties(name)
+
def replaceProperty(self, property):
"""
Add or replace a property in this component.
@@ -410,6 +524,7 @@
self._pycard.removeProperties(property.name())
self.addProperty(property)
+
def resourceUID(self):
"""
@return: the UID of the subcomponents in this component.
@@ -421,6 +536,7 @@
return self._resource_uid
+
def resourceKind(self):
"""
@return: the kind of the subcomponents in this component.
@@ -432,6 +548,7 @@
return self._resource_kind
+
def resourceMemberAddresses(self):
"""
@return: an iterable of X-ADDRESSBOOKSERVER-MEMBER property values
@@ -440,6 +557,7 @@
return [prop.value() for prop in list(self.properties("X-ADDRESSBOOKSERVER-MEMBER"))]
+
def validVCardData(self, doFix=True, doRaise=True):
"""
@return: tuple of fixed, unfixed issues
@@ -454,17 +572,19 @@
if unfixed:
log.debug("vCard data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
if doRaise:
- raise InvalidVCardDataError("Calendar data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
+ raise InvalidVCardDataError("Address data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
if fixed:
log.debug("vCard data had fixable problems:\n %s" % ("\n ".join(fixed),))
return fixed, unfixed
+
def validForCardDAV(self):
"""
@raise ValueError: if the given vcard data is not valid.
"""
- if self.name() != "VCARD": raise InvalidVCardDataError("Not a vcard")
+ if self.name() != "VCARD":
+ raise InvalidVCardDataError("Not a vcard")
version = self.propertyValue("VERSION")
if version != "3.0":
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -24,17 +24,21 @@
except ImportError:
from xml.parsers.expat import ExpatError as XMLParseError
+
+
# Utilities for working with ElementTree
def readXMLString(xmldata, expectedRootTag=None):
io = StringIO.StringIO(xmldata)
return readXML(io, expectedRootTag)
+
+
def readXML(xmlfile, expectedRootTag=None):
"""
Read in XML data from a file and parse into ElementTree. Optionally verify
the root node is what we expect.
-
+
@param xmlfile: file to read from
@type xmlfile: C{File}
@param expectedRootTag: root tag (qname) to test or C{None}
@@ -52,14 +56,18 @@
root = etree.getroot()
if root.tag != expectedRootTag:
raise ValueError("Ignoring file '%s' because it is not a %s file" % (xmlfile, expectedRootTag,))
-
+
return etree, etree.getroot()
+
+
def elementToXML(element):
return XML.tostring(element, "utf-8")
+
+
def writeXML(xmlfile, root):
-
+
data = """<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE %s SYSTEM "%s.dtd">
@@ -69,7 +77,7 @@
# Generate indentation
def _indentNode(node, level=0):
-
+
if node.text is not None and node.text.strip():
return
elif len(node):
@@ -87,27 +95,35 @@
with open(xmlfile, "w") as f:
f.write(data)
+
+
def newElementTreeWithRoot(roottag):
root = createElement(roottag)
etree = XML.ElementTree(root)
-
+
return etree, root
+
+
def createElement(tag, text=None, **attrs):
child = XML.Element(tag, attrs)
child.text = text
return child
+
+
def addSubElement(parent, tag, text=None):
child = XML.SubElement(parent, tag)
child.text = text
return child
+
+
def changeSubElementText(parent, tag, text):
-
+
child = parent.find(tag)
if child is not None:
child.text = text
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -54,6 +54,7 @@
RDATE:20100808T000000
RDATE:20110731T000000
RDATE:20120720T030000
+RDATE:20120930T030000
RDATE:20130707T030000
RDATE:20140629T030000
RDATE:20150618T030000
@@ -61,6 +62,9 @@
RDATE:20170527T030000
RDATE:20180516T030000
RDATE:20190506T030000
+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
TZNAME:WET
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
@@ -88,17 +92,24 @@
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20120429T020000
-RRULE:FREQ=YEARLY;UNTIL=20190428T020000Z;BYDAY=-1SU;BYMONTH=4
+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:WEST
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
END:DAYLIGHT
BEGIN:STANDARD
-DTSTART:20120930T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9
+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:WET
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,9 +15,84 @@
BEGIN:STANDARD
DTSTART:19760414T000000
RDATE:19760414T000000
-TZNAME:WET
+TZNAME:WEST
TZOFFSETFROM:-0100
TZOFFSETTO:+0000
END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19760501T000000
+RRULE:FREQ=YEARLY;UNTIL=19770501T000000Z;BYMONTH=5
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19760801T000000
+RDATE:19760801T000000
+RDATE:19770928T000000
+RDATE:19780804T000000
+RDATE:20080901T000000
+RDATE:20090821T000000
+RDATE:20100808T000000
+RDATE:20110731T000000
+RDATE:20120720T030000
+RDATE:20120930T030000
+RDATE:20130707T030000
+RDATE:20140629T030000
+RDATE:20150618T030000
+RDATE:20160607T030000
+RDATE:20170527T030000
+RDATE:20180516T030000
+RDATE:20190506T030000
+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19780601T000000
+RDATE:19780601T000000
+RDATE:20080601T000000
+RDATE:20090601T000000
+RDATE:20100502T000000
+RDATE:20110403T000000
+RDATE:20120820T020000
+RDATE:20130810T020000
+RDATE:20140729T020000
+RDATE:20150718T020000
+RDATE:20160707T020000
+RDATE:20170626T020000
+RDATE:20180615T020000
+RDATE:20190605T020000
+RDATE:20200524T020000
+RDATE:20210513T020000
+RDATE:20220503T020000
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:20120429T020000
+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,6 +20,7 @@
RDATE:19850406T000000
RDATE:19860404T000000
RDATE:19970404T000000
+RDATE:20130329T010000
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
@@ -82,23 +83,10 @@
BEGIN:STANDARD
DTSTART:19971004T000000
RDATE:19971004T000000
+RDATE:20131025T020000
TZNAME:EET
TZOFFSETFROM:+0200
TZOFFSETTO:+0200
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
END:VTIMEZONE
END:VCALENDAR
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -51,6 +51,7 @@
RDATE:19870214T000000
RDATE:19880207T000000
RDATE:19940220T000000
+RDATE:20131110T000000
TZNAME:ACT
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -49,6 +49,7 @@
RDATE:19860315T000000
RDATE:19870214T000000
RDATE:19880207T000000
+RDATE:20131110T000000
TZNAME:ACT
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -49,6 +49,7 @@
RDATE:19860315T000000
RDATE:19870214T000000
RDATE:19880207T000000
+RDATE:20131110T000000
TZNAME:ACT
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -49,6 +49,7 @@
RDATE:19860315T000000
RDATE:19870214T000000
RDATE:19880207T000000
+RDATE:20131110T000000
TZNAME:ACT
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics 2013-12-14 06:28:16 UTC (rev 12110)
@@ -20,6 +20,7 @@
RDATE:19850406T000000
RDATE:19860404T000000
RDATE:19970404T000000
+RDATE:20130329T010000
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
@@ -82,23 +83,10 @@
BEGIN:STANDARD
DTSTART:19971004T000000
RDATE:19971004T000000
+RDATE:20131025T020000
TZNAME:EET
TZOFFSETFROM:+0200
TZOFFSETTO:+0200
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
END:VTIMEZONE
END:VCALENDAR
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml 2013-12-14 06:28:16 UTC (rev 12110)
@@ -2,7 +2,7 @@
<!DOCTYPE timezones SYSTEM "timezones.dtd">
<timezones>
- <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
<timezone>
<tzid>Africa/Abidjan</tzid>
<dtstamp>2011-10-05T11:50:21Z</dtstamp>
@@ -78,8 +78,8 @@
</timezone>
<timezone>
<tzid>Africa/Casablanca</tzid>
- <dtstamp>2013-07-11T02:11:45Z</dtstamp>
- <md5>b4e345b053c4699911078dcd16854bab</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>4f58dbcb4f7e6dfa7af3d710aeff97b4</md5>
</timezone>
<timezone>
<tzid>Africa/Ceuta</tzid>
@@ -113,8 +113,8 @@
</timezone>
<timezone>
<tzid>Africa/El_Aaiun</tzid>
- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>494500808e8542fd83e4706654c43545</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>7933d45461e9f988ebfc9d062ce894e4</md5>
</timezone>
<timezone>
<tzid>Africa/Freetown</tzid>
@@ -264,9 +264,9 @@
</timezone>
<timezone>
<tzid>Africa/Tripoli</tzid>
- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
<alias>Libya</alias>
- <md5>f59e5f16eec995c112b8b27580922fdd</md5>
+ <md5>6e8040bfd898654905bfd0a49c64e365</md5>
</timezone>
<timezone>
<tzid>Africa/Tunis</tzid>
@@ -570,8 +570,8 @@
</timezone>
<timezone>
<tzid>America/Eirunepe</tzid>
- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>d3c4df84162ebac445e1c2dcdb81f3b2</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>cc34b66260adda629311a54df088470b</md5>
</timezone>
<timezone>
<tzid>America/El_Salvador</tzid>
@@ -964,8 +964,8 @@
</timezone>
<timezone>
<tzid>America/Porto_Acre</tzid>
- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>ce88b8461b7217e1d9ec8f4dfac34670</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>b2b04e3c9c1dab12a3764b99dd8536fc</md5>
</timezone>
<timezone>
<tzid>America/Porto_Velho</tzid>
@@ -1006,10 +1006,10 @@
</timezone>
<timezone>
<tzid>America/Rio_Branco</tzid>
- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
<alias>America/Porto_Acre</alias>
<alias>Brazil/Acre</alias>
- <md5>c41ff8b67906037ce014d42019e5831f</md5>
+ <md5>51273c4521cd00a8ecdcff336f0c9503</md5>
</timezone>
<timezone>
<tzid>America/Rosario</tzid>
@@ -1885,8 +1885,8 @@
</timezone>
<timezone>
<tzid>Brazil/Acre</tzid>
- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>b034140cdfc442b0f989f6a6dd6ec620</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>ad33124888f5c5dd9bed9317ef0bb953</md5>
</timezone>
<timezone>
<tzid>Brazil/DeNoronha</tzid>
@@ -2641,8 +2641,8 @@
</timezone>
<timezone>
<tzid>Libya</tzid>
- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
- <md5>f514b497bd861c14aa21612f9101c50c</md5>
+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>e6ac54ea0ab33dd6fd125a71fc34cf46</md5>
</timezone>
<timezone>
<tzid>MET</tzid>
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1 +1 @@
-IANA Timezone Registry: 2013f
\ No newline at end of file
+IANA Timezone Registry: 2013h
\ No newline at end of file
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -214,7 +214,13 @@
def __delitem__(self, key):
# Handle per-user behavior
- if self.isGlobalProperty(key):
+ if self.isShadowableProperty(key):
+ try:
+ self._delitem_uid(key, self._perUser)
+ except KeyError:
+ # It is OK for shadowable delete to fail
+ pass
+ elif self.isGlobalProperty(key):
self._delitem_uid(key, self._defaultUser)
else:
self._delitem_uid(key, self._perUser)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -206,6 +206,41 @@
@inlineCallbacks
+ def test_peruserShadow_delete(self):
+ """
+ Delete a shadowable property that has not been overridden by the sharee.
+ """
+
+ name = propertyName("shadow")
+
+ self.propertyStore1.setSpecialProperties((name,), ())
+ self.propertyStore2.setSpecialProperties((name,), ())
+
+ value1 = propertyValue("Hello, World1!")
+
+ self.propertyStore1[name] = value1
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+
+ del self.propertyStore2[name]
+ yield self._changed(self.propertyStore2)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+
+ del self.propertyStore1[name]
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), None)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.failIf(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+
+
+ @inlineCallbacks
def test_peruser_global(self):
name = propertyName("global")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -41,8 +41,8 @@
from twext.web2.dav.resource import TwistedGETContentMD5
from twext.web2.http_headers import generateContentType, MimeType
-from twistedcaldav import caldavxml, customxml
-from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque
+from twistedcaldav import caldavxml, customxml, ical
+from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque, Transparent
from twistedcaldav.config import config
from twistedcaldav.ical import InvalidICalendarDataError
@@ -84,6 +84,14 @@
_topPath = "calendars"
_notifierPrefix = "CalDAV"
+ _componentCalendarName = {
+ "VEVENT": "calendar",
+ "VTODO": "tasks",
+ "VJOURNAL": "journals",
+ "VAVAILABILITY": "available",
+ "VPOLL": "polls",
+ }
+
def __init__(self, uid, path, calendarStore, transaction):
super(CalendarHome, self).__init__(uid, path, calendarStore, transaction)
@@ -177,20 +185,19 @@
def createdHome(self):
- # Default calendar
- defaultCal = self.createCalendarWithName("calendar")
- props = defaultCal.properties()
- props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque())
-
# Check whether components type must be separate
if config.RestrictCalendarsToOneComponentType:
- defaultCal.setSupportedComponents("VEVENT")
+ for name in ical.allowedStoreComponents:
+ cal = self.createCalendarWithName(self._componentCalendarName[name])
+ cal.setSupportedComponents(name)
+ props = cal.properties()
+ if name not in ("VEVENT", "VAVAILABILITY",):
+ props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Transparent())
+ else:
+ props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque())
+ else:
+ cal = self.createCalendarWithName("calendar")
- # Default tasks
- defaultTasks = self.createCalendarWithName("tasks")
- props = defaultTasks.properties()
- defaultTasks.setSupportedComponents("VTODO")
-
self.createCalendarWithName("inbox")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -55,9 +55,9 @@
from twistedcaldav.config import config
from twistedcaldav.memcachepool import CachePoolUserMixIn
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
log = Logger()
@@ -341,7 +341,7 @@
maxDate = maxDate.duplicate()
maxDate.setDateOnly(True)
if isStartDate:
- maxDate += PyCalendarDuration(days=365)
+ maxDate += Duration(days=365)
self.testAndUpdateIndex(maxDate)
else:
# We cannot handle this filter in an indexed search
@@ -671,7 +671,7 @@
if master is None or not calendar.isRecurring():
# When there is no master we have a set of overridden components - index them all.
# When there is one instance - index it.
- expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ expand = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
doInstanceIndexing = True
else:
# If migrating or re-creating or config option for delayed indexing is off, always index
@@ -682,8 +682,8 @@
# by default. This is a caching parameter which affects the size of the index;
# it does not affect search results beyond this period, but it may affect
# performance of such a search.
- expand = (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
+ expand = (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandAheadDays))
if expand_until and expand_until > expand:
expand = expand_until
@@ -700,8 +700,8 @@
# occurrences into some obscenely far-in-the-future date, so we cap the caching
# period. Searches beyond this period will always be relatively expensive for
# resources with occurrences beyond this period.
- if expand > (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
+ if expand > (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandMaxDays)):
raise IndexedSearchException()
# Always do recurrence expansion even if we do not intend to index - we need this to double-check the
@@ -716,7 +716,7 @@
# Now coerce indexing to off if needed
if not doInstanceIndexing:
instances = None
- recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
self._delete_from_db(name, uid, False)
@@ -782,8 +782,8 @@
# Special - for unbounded recurrence we insert a value for "infinity"
# that will allow an open-ended time-range to always match it.
if calendar.isRecurringUnbounded():
- start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(2100, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
float = 'N'
self._db_execute(
"""
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -53,7 +53,7 @@
@inlineCallbacks
def getCalendarUser(self, cuaddr, principal):
- # If we have a principal always treat the user as local or partitioned
+ # If we have a principal always treat the user as local
if principal:
returnValue(calendarUserFromPrincipal(cuaddr, principal))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -26,8 +26,7 @@
from twistedcaldav.config import config
from txdav.base.propertystore.base import PropertyName
-from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, \
- PartitionedCalendarUser, OtherServerCalendarUser
+from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, OtherServerCalendarUser
from txdav.caldav.datastore.scheduling.delivery import DeliveryService
from txdav.caldav.datastore.scheduling.freebusy import processAvailabilityFreeBusy, \
generateFreeBusyInfo, buildFreeBusyResult
@@ -99,7 +98,7 @@
uid = self.scheduler.calendar.resourceUID()
organizerPrincipal = None
- if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,):
+ if type(self.scheduler.organizer) in (LocalCalendarUser, OtherServerCalendarUser,):
organizerPrincipal = self.scheduler.organizer.principal.uid
for recipient in self.recipients:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -101,6 +101,14 @@
"No principal for originator",
))
else:
+ if not (originatorPrincipal.calendarsEnabled() and originatorPrincipal.thisServer()):
+ log.error("Originator not enabled or hosted on this server: %s" % (self.originator,))
+ raise HTTPError(self.errorResponse(
+ responsecode.FORBIDDEN,
+ self.errorElements["originator-denied"],
+ "Originator cannot be scheduled",
+ ))
+
self.originator = LocalCalendarUser(self.originator, originatorPrincipal)
@@ -127,8 +135,8 @@
else:
# Map recipient to their inbox
inbox = None
- if principal.calendarsEnabled() and principal.thisServer():
- if principal.locallyHosted():
+ if principal.calendarsEnabled():
+ if principal.thisServer():
recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
if recipient_home:
inbox = (yield recipient_home.calendarWithName("inbox"))
@@ -138,7 +146,7 @@
if inbox:
results.append(calendarUserFromPrincipal(recipient, principal, inbox))
else:
- log.error("No schedule inbox for principal: %s" % (principal,))
+ log.error("Recipient not enabled for calendaring: %s" % (principal,))
results.append(InvalidCalendarUser(recipient))
self.recipients = results
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.python.clsprop import classproperty
@@ -45,7 +45,7 @@
self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
yield self.populate()
- self.now = PyCalendarDateTime.getNowUTC()
+ self.now = DateTime.getNowUTC()
self.now.setHHMMSS(0, 0, 0)
self.now_12H = self.now.duplicate()
@@ -170,9 +170,9 @@
scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
result = (yield scheduler.doSchedulingViaPOST("mailto:user01 at example.com", ["mailto:user01 at example.com", ], Component.fromString(data_request)))
self.assertEqual(len(result.responses), 1)
- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01 at example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01 at example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
@inlineCallbacks
@@ -227,9 +227,9 @@
scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
result = (yield scheduler.doSchedulingViaPOST("mailto:user01 at example.com", ["mailto:user01 at example.com", ], Component.fromString(data_request)))
self.assertEqual(len(result.responses), 1)
- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01 at example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01 at example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
@inlineCallbacks
@@ -294,6 +294,6 @@
scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
result = (yield scheduler.doSchedulingViaPOST("mailto:user01 at example.com", ["mailto:user01 at example.com", ], Component.fromString(data_request)))
self.assertEqual(len(result.responses), 1)
- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01 at example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01 at example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,7 +21,6 @@
__all__ = [
"LocalCalendarUser",
- "PartitionedCalendarUser",
"OtherServerCalendarUser",
"RemoteCalendarUser",
"EmailCalendarUser",
@@ -53,19 +52,6 @@
-class PartitionedCalendarUser(CalendarUser):
-
- def __init__(self, cuaddr, principal):
- self.cuaddr = cuaddr
- self.principal = principal
- self.serviceType = DeliveryService.serviceType_ischedule
-
-
- def __str__(self):
- return "Partitioned calendar user: %s" % (self.cuaddr,)
-
-
-
class OtherServerCalendarUser(CalendarUser):
def __init__(self, cuaddr, principal):
@@ -145,9 +131,7 @@
Get the appropriate calendar user address class for the provided principal.
"""
- if principal.locallyHosted():
+ if principal.thisServer():
return LocalCalendarUser(recipient, principal, inbox)
- elif principal.thisServer():
- return PartitionedCalendarUser(recipient, principal)
else:
return OtherServerCalendarUser(recipient, principal)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,10 +14,10 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
from twext.python.log import Logger
@@ -68,8 +68,8 @@
if entry:
# Offset one day at either end to account for floating
- cached_start = entry.timerange.start + PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
- cached_end = entry.timerange.end - PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_start = entry.timerange.start + Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_end = entry.timerange.end - Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
# Verify that the requested time range lies within the cache time range
if compareDateTime(timerange.end, cached_end) <= 0 and compareDateTime(timerange.start, cached_start) >= 0:
@@ -187,8 +187,8 @@
logItems["fb-uncached"] = logItems.get("fb-uncached", 0) + 1
# We want to cache a large range of time based on the current date
- cache_start = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=0 - config.FreeBusyCacheDaysBack))
- cache_end = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=config.FreeBusyCacheDaysForward))
+ cache_start = normalizeToUTC(DateTime.getToday() + Duration(days=0 - config.FreeBusyCacheDaysBack))
+ cache_end = normalizeToUTC(DateTime.getToday() + Duration(days=config.FreeBusyCacheDaysForward))
# If the requested time range would fit in our allowed cache range, trigger the cache creation
if compareDateTime(timerange.start, cache_start) >= 0 and compareDateTime(timerange.end, cache_end) <= 0:
@@ -227,7 +227,7 @@
logItems["fb-cached"] = logItems.get("fb-cached", 0) + 1
# Determine appropriate timezone (UTC is the default)
- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
# We care about separate instances for VEVENTs only
aggregated_resources = {}
@@ -270,15 +270,15 @@
if float == 'Y':
fbstart.setTimezone(tzinfo)
else:
- fbstart.setTimezone(PyCalendarTimezone(utc=True))
+ fbstart.setTimezone(Timezone(utc=True))
fbend = parseSQLTimestampToPyCalendar(end)
if float == 'Y':
fbend.setTimezone(tzinfo)
else:
- fbend.setTimezone(PyCalendarTimezone(utc=True))
+ fbend.setTimezone(Timezone(utc=True))
# Clip instance to time range
- clipped = clipPeriod(PyCalendarPeriod(fbstart, duration=fbend - fbstart), PyCalendarPeriod(timerange.start, timerange.end))
+ clipped = clipPeriod(Period(fbstart, duration=fbend - fbstart), Period(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -364,7 +364,7 @@
@param timerange: the time-range in which to expand
@type timerange: L{TimeRange}
@param tzinfo: timezone for floating time calculations
- @type tzinfo: L{PyCalendarTimezone}
+ @type tzinfo: L{Timezone}
"""
# First expand the component
@@ -404,7 +404,7 @@
@param calendar: the L{Component} that is the VCALENDAR containing the VEVENT's.
@param fbinfo: the tuple used to store the three types of fb data.
@param timerange: the time range to restrict free busy data to.
- @param tzinfo: the L{PyCalendarTimezone} for the timezone to use for floating/all-day events.
+ @param tzinfo: the L{Timezone} for the timezone to use for floating/all-day events.
"""
# Expand out the set of instances for the event with in the required range
@@ -449,10 +449,10 @@
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = PyCalendarPeriod(fbstart, duration=fbend - fbstart)
+ period = Period(fbstart, duration=fbend - fbstart)
else:
- period = PyCalendarPeriod(fbstart, fbend)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ period = Period(fbstart, fbend)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -490,7 +490,7 @@
assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
for period in fb.value():
# Clip period for this instance
- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(timerange.start, timerange.end))
+ clipped = clipPeriod(period.getValue(), Period(timerange.start, timerange.end))
if clipped:
fbinfo[fbtype_mapper.get(fbtype, 0)].append(clipped)
@@ -509,12 +509,12 @@
# Get overall start/end
start = vav.getStartDateUTC()
if start is None:
- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
end = vav.getEndDateUTC()
if end is None:
- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- period = PyCalendarPeriod(start, end)
- overall = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ period = Period(start, end)
+ overall = clipPeriod(period, Period(timerange.start, timerange.end))
if overall is None:
continue
@@ -526,10 +526,10 @@
last_end = timerange.start
for period in periods:
if last_end < period.getStart():
- busyperiods.append(PyCalendarPeriod(last_end, period.getStart()))
+ busyperiods.append(Period(last_end, period.getStart()))
last_end = period.getEnd()
if last_end < timerange.end:
- busyperiods.append(PyCalendarPeriod(last_end, timerange.end))
+ busyperiods.append(Period(last_end, timerange.end))
# Add to actual results mapped by busy type
fbtype = vav.propertyValue("BUSYTYPE")
@@ -576,10 +576,10 @@
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = PyCalendarPeriod(start, duration=end - start)
+ period = Period(start, duration=end - start)
else:
- period = PyCalendarPeriod(start, end)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
+ period = Period(start, end)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
if clipped:
periods.append(clipped)
@@ -622,7 +622,7 @@
fb.addProperty(attendee)
fb.addProperty(Property("DTSTART", timerange.start))
fb.addProperty(Property("DTEND", timerange.end))
- fb.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
if len(fbinfo[0]) != 0:
fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
if len(fbinfo[1]) != 0:
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,8 +16,8 @@
from difflib import unified_diff
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
from twext.python.log import Logger
@@ -484,7 +484,7 @@
# If PARTSTAT was changed by the attendee, add a timestamp if needed
if config.Scheduling.Options.TimestampAttendeePartStatChanges:
- serverAttendee.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
+ serverAttendee.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
replyNeeded = True
@@ -516,6 +516,10 @@
if comp.name() == "VALARM":
serverComponent.addComponent(comp)
+ # VPOLL
+ if serverComponent.name() == "VPOLL":
+ replyNeeded = self._transferVPOLLData(serverComponent, clientComponent)
+
return True, replyNeeded
@@ -556,6 +560,44 @@
return True
+ def _transferVPOLLData(self, serverComponent, clientComponent):
+
+ changed = False
+
+ # Get the VOTER properties in sub-components of the VPOLL as set by the attendee
+ poll_items = {}
+ for component in clientComponent.subcomponents():
+ poll_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_id is not None:
+ poll_items[poll_id] = component.getVoterProperty((self.attendee,))
+
+ # Transfer attendee data with the master set
+ for component in serverComponent.subcomponents():
+ poll_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_id is not None:
+ voter = component.getVoterProperty((self.attendee,))
+ attendee_voter = poll_items.get(poll_id)
+ if attendee_voter is None:
+ if voter is not None:
+ component.removeProperty(voter)
+ changed = True
+ elif voter is None:
+ component.addProperty(attendee_voter)
+ changed = True
+ else:
+ for paramname in ("RESPONSE",):
+ paramvalue = attendee_voter.parameterValue(paramname)
+ if paramvalue is None:
+ voter.removeParameter(paramname)
+ changed = True
+ else:
+ if paramvalue != voter.parameterValue(paramname):
+ voter.setParameter(paramname, paramvalue)
+ changed = True
+
+ return changed
+
+
def _checkInvalidChanges(self, serverComponent, clientComponent, declines):
# Properties we care about: DTSTART, DTEND, DURATION, RRULE, RDATE, EXDATE
@@ -585,12 +627,12 @@
def _getNormalizedDateTimeProperties(self, component):
# Basic time properties
- if component.name() in ("VEVENT", "VJOURNAL",):
+ if component.name() in ("VEVENT", "VJOURNAL", "VPOLL"):
dtstart = component.getProperty("DTSTART")
dtend = component.getProperty("DTEND")
duration = component.getProperty("DURATION")
- timeRange = PyCalendarPeriod(
+ timeRange = Period(
start=dtstart.value() if dtstart is not None else None,
end=dtend.value() if dtend is not None else None,
duration=duration.value() if duration is not None else None,
@@ -602,16 +644,19 @@
duration = component.getProperty("DURATION")
if dtstart or duration:
- timeRange = PyCalendarPeriod(
+ timeRange = Period(
start=dtstart.value() if dtstart is not None else None,
duration=duration.value() if duration is not None else None,
)
else:
- timeRange = PyCalendarPeriod()
+ timeRange = Period()
newdue = component.getProperty("DUE")
if newdue is not None:
newdue = newdue.value().duplicate().adjustToUTC()
+ else:
+ timeRange = Period()
+ newdue = None
# Recurrence rules - we need to normalize the order of the value parts
newrrules = set()
@@ -627,7 +672,7 @@
rdates = component.properties("RDATE")
for rdate in rdates:
for value in rdate.value():
- if isinstance(value, PyCalendarDateTime):
+ if isinstance(value, DateTime):
value = value.duplicate().adjustToUTC()
newrdates.add(value)
@@ -734,6 +779,7 @@
comp.normalizePropertyValueLists("EXDATE")
comp.removePropertyParameters("ORGANIZER", ("SCHEDULE-STATUS",))
comp.removePropertyParameters("ATTENDEE", ("SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
+ comp.removePropertyParameters("VOTER", ("SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
comp.removeAlarms()
comp.normalizeAll()
comp.normalizeAttachments()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twistedcaldav.ical import Property
@@ -34,10 +34,10 @@
"""
self.threshold = threshold
- self.past = PyCalendarDateTime.getNowUTC()
+ self.past = DateTime.getNowUTC()
self.past.setHHMMSS(0, 0, 0)
self.past.offsetDay(-past)
- self.now = PyCalendarDateTime.getNowUTC()
+ self.now = DateTime.getNowUTC()
self.now.setHHMMSS(0, 0, 0)
self.now.offsetDay(-1)
@@ -91,7 +91,7 @@
@type ical: L{Component}
@return: recurrence-id of the split
- @rtype: L{PyCalendarDateTime}
+ @rtype: L{DateTime}
"""
# Find the instance RECURRENCE-ID where a split is going to happen
@@ -124,7 +124,7 @@
@type ical: L{Component}
@param rid: recurrence-id where the split should occur, or C{None} to determine it here
- @type rid: L{PyCalendarDateTime} or C{None}
+ @type rid: L{DateTime} or C{None}
@param olderUID: UID to use for the split off component, or C{None} to generate one here
@type olderUID: C{str} or C{None}
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -26,8 +26,8 @@
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import email.utils
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
from twext.enterprise.dal.record import fromTable
from twext.enterprise.queue import WorkItem
from twext.python.log import Logger
@@ -320,8 +320,8 @@
"""
if onlyAfter is None:
- duration = PyCalendarDuration(days=self.suppressionDays)
- onlyAfter = PyCalendarDateTime.getNowUTC() - duration
+ duration = Duration(days=self.suppressionDays)
+ onlyAfter = DateTime.getNowUTC() - duration
icaluid = calendar.resourceUID()
method = calendar.propertyValue("METHOD")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -64,7 +64,7 @@
yield delivery.generateSchedulingResponses()
self.assertEqual(len(responses.responses), 1)
- self.assertEqual(str(responses.responses[0].children[1]), iTIPRequestStatus.SERVICE_UNAVAILABLE)
+ self.assertEqual(str(responses.responses[0].reqstatus), iTIPRequestStatus.SERVICE_UNAVAILABLE)
@inlineCallbacks
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -300,7 +300,7 @@
yield txn.commit()
self.assertEquals(
"1.2;Scheduling message has been delivered",
- result.responses[0].children[1].toString()
+ result.responses[0].reqstatus.toString()
)
@@ -333,7 +333,7 @@
yield txn.commit()
self.assertEquals(
"3.7;Invalid Calendar User",
- result.responses[0].children[1].toString()
+ result.responses[0].reqstatus.toString()
)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -17,7 +17,7 @@
from cStringIO import StringIO
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twisted.internet.defer import inlineCallbacks, succeed
from twisted.trial import unittest
@@ -275,7 +275,7 @@
inputOriginator,
inputRecipient,
Component.fromString(inputCalendar.replace("\n", "\r\n")),
- onlyAfter=PyCalendarDateTime(2010, 1, 1, 0, 0, 0)
+ onlyAfter=DateTime(2010, 1, 1, 0, 0, 0)
)
yield txn.commit()
@@ -319,7 +319,7 @@
inputOriginator,
inputRecipient,
Component.fromString(inputCalendar.replace("\n", "\r\n")),
- onlyAfter=PyCalendarDateTime(2021, 1, 1, 0, 0, 0)
+ onlyAfter=DateTime(2021, 1, 1, 0, 0, 0)
)
yield txn.commit()
self.assertFalse(self.sender.smtpSender.sendMessageCalled)
@@ -381,7 +381,7 @@
txn = self.store.newTransaction()
yield self.sender.outbound(txn, inputOriginator, inputRecipient,
Component.fromString(inputCalendar.replace("\n", "\r\n")),
- onlyAfter=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+ onlyAfter=DateTime(2010, 1, 1, 0, 0, 0))
# Verify we didn't create a new token...
txn = self.store.newTransaction()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -21,7 +21,6 @@
from twext.web2 import responsecode
from twext.web2.http import HTTPError
-from twistedcaldav import caldavxml
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
from twistedcaldav.ical import Property
@@ -29,7 +28,7 @@
from txdav.caldav.datastore.scheduling import addressmapping
from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
from txdav.caldav.datastore.scheduling.cuaddress import InvalidCalendarUser, \
- LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser, \
+ LocalCalendarUser, OtherServerCalendarUser, \
normalizeCUAddr
from txdav.caldav.datastore.scheduling.icaldiff import iCalDiff
from txdav.caldav.datastore.scheduling.itip import iTipGenerator, iTIPRequestStatus
@@ -251,7 +250,7 @@
# to create new scheduling resources.
if self.action == "create":
if self.organizerPrincipal and not self.organizerPrincipal.enabledAsOrganizer():
- log.error("ORGANIZER not allowed to be an Organizer: %s" % (self.organizer,))
+ log.error("ORGANIZER not allowed to be an Organizer: {organizer}", organizer=self.organizer)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "organizer-allowed"),
@@ -427,7 +426,7 @@
self.organizer = self.calendar.validOrganizerForScheduling()
except ValueError:
# We have different ORGANIZERs in the same iCalendar object - this is an error
- log.error("Only one ORGANIZER is allowed in an iCalendar object:\n%s" % (self.calendar,))
+ log.error("Only one ORGANIZER is allowed in an iCalendar object:\n{calendar}", calendar=self.calendar)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "single-organizer"),
@@ -462,7 +461,7 @@
# Check for matching resource somewhere else in the home
foundElsewhere = (yield self.calendar_home.hasCalendarResourceUIDSomewhereElse(self.uid, check_resource, mode))
if foundElsewhere is not None:
- log.debug("Implicit - found component with same UID in a different collection: %s" % (check_resource,))
+ log.debug("Implicit - found component with same UID in a different collection: {resource}", resource=check_resource)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "unique-scheduling-object-resource"),
@@ -531,7 +530,7 @@
# Check for a delete
if self.action == "remove":
- log.debug("Implicit - organizer '%s' is removing UID: '%s'" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is removing UID: '{uid}'", organizer=self.organizer, uid=self.uid)
self.oldcalendar = self.calendar
# Cancel all attendees
@@ -557,16 +556,16 @@
no_change, self.changed_rids, self.needs_action_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
if no_change:
if reinvites:
- log.debug("Implicit - organizer '%s' is re-inviting UID: '%s', attendees: %s" % (self.organizer, self.uid, ", ".join(reinvites)))
+ log.debug("Implicit - organizer '{organizer}' is re-inviting UID: '{uid}', attendees: {attendees}", organizer=self.organizer, uid=self.uid, attendees=", ".join(reinvites))
self.reinvites = reinvites
else:
# Nothing to do
- log.debug("Implicit - organizer '%s' is modifying UID: '%s' but change is not significant" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is modifying UID: '{uid}' but change is not significant", organizer=self.organizer, uid=self.uid)
returnValue(None)
else:
# Do not change PARTSTATs for a split operation
if self.split_details is None:
- log.debug("Implicit - organizer '%s' is modifying UID: '%s'" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is modifying UID: '{uid}'", organizer=self.organizer, uid=self.uid)
for rid in self.needs_action_rids:
comp = self.calendar.overriddenComponent(rid)
@@ -587,7 +586,7 @@
attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
else:
- log.debug("Implicit - organizer '%s' is splitting UID: '%s'" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is splitting UID: '{uid}'", organizer=self.organizer, uid=self.uid)
# Check for removed attendees
if not recurrence_reschedule:
@@ -601,10 +600,10 @@
elif self.action == "create":
if self.split_details is None:
- log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is creating UID: '{uid}'", organizer=self.organizer, uid=self.uid)
self.coerceAttendeesPartstatOnCreate()
else:
- log.debug("Implicit - organizer '%s' is creating a split UID: '%s'" % (self.organizer, self.uid))
+ log.debug("Implicit - organizer '{organizer}' is creating a split UID: '{uid}'", organizer=self.organizer, uid=self.uid)
self.needs_sequence_change = False
# Always set RSVP=TRUE for any NEEDS-ACTION
@@ -698,7 +697,7 @@
oldOrganizer = self.oldcalendar.getOrganizer()
newOrganizer = self.calendar.getOrganizer()
if oldOrganizer != newOrganizer:
- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-organizer-change"),
@@ -906,7 +905,7 @@
if cuaddr not in coerced:
attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(cuaddr)
attendeeAddress = (yield addressmapping.mapper.getCalendarUser(cuaddr, attendeePrincipal))
- local_attendee = type(attendeeAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
+ local_attendee = type(attendeeAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
coerced[cuaddr] = local_attendee
if coerced[cuaddr]:
attendee.removeParameter("SCHEDULE-AGENT")
@@ -977,7 +976,7 @@
scheduler = self.makeScheduler()
# Do the PUT processing
- log.info("Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'" % (self.organizer, attendee, self.uid, rids))
+ log.info("Implicit CANCEL - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}', RIDs: '{rids}'", organizer=self.organizer, attendee=attendee, uid=self.uid, rids=rids)
response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
self.handleSchedulingResponse(response, True)
@@ -1034,7 +1033,7 @@
scheduler = self.makeScheduler()
# Do the PUT processing
- log.info("Implicit REQUEST - organizer: '%s' to attendee: '%s', UID: '%s'" % (self.organizer, attendee, self.uid,))
+ log.info("Implicit REQUEST - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}'", organizer=self.organizer, attendee=attendee, uid=self.uid)
response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
self.handleSchedulingResponse(response, True)
@@ -1047,17 +1046,17 @@
# Map each recipient in the response to a status code
responses = {}
+ propname = self.calendar.mainComponent().recipientPropertyName() if is_organizer else "ORGANIZER"
for item in response.responses:
- assert isinstance(item, caldavxml.Response), "Wrong element in response"
- recipient = str(item.children[0].children[0])
- status = str(item.children[1])
+ recipient = str(item.recipient.children[0])
+ status = str(item.reqstatus)
responses[recipient] = status
# Now apply to each ATTENDEE/ORGANIZER in the original data
self.calendar.setParameterToValueForPropertyWithValue(
"SCHEDULE-STATUS",
status.split(";")[0],
- "ATTENDEE" if is_organizer else "ORGANIZER",
+ propname,
recipient)
@@ -1069,19 +1068,19 @@
if self.action == "remove":
if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
- log.debug("Implicit - attendee '%s' is removing cancelled UID: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is removing cancelled UID: '{uid}'", attendee=self.attendee, uid=self.uid)
# Nothing else to do
elif doScheduling:
# If attendee is already marked as declined in all components - nothing to do
attendees = self.calendar.getAttendeeProperties((self.attendee,))
if all([attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "DECLINED" for attendee in attendees]):
- log.debug("Implicit - attendee '%s' is removing fully declined UID: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is removing fully declined UID: '{uid}'", attendee=self.attendee, uid=self.uid)
# Nothing else to do
else:
- log.debug("Implicit - attendee '%s' is cancelling UID: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is cancelling UID: '{uid}'", attendee=self.attendee, uid=self.uid)
yield self.scheduleCancelWithOrganizer()
else:
- log.debug("Implicit - attendee '%s' is removing UID without server scheduling: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is removing UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
# Nothing else to do
returnValue(None)
@@ -1092,7 +1091,7 @@
oldOrganizer = self.oldcalendar.getOrganizer()
newOrganizer = self.calendar.getOrganizer()
if oldOrganizer != newOrganizer:
- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1107,7 +1106,7 @@
# If Organizer copy exists we cannot allow SCHEDULE-AGENT=CLIENT or NONE
if not doScheduling:
- log.error("Attendee '%s' is not allowed to change SCHEDULE-AGENT on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
+ log.error("Attendee '{attendee}' is not allowed to change SCHEDULE-AGENT on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1121,11 +1120,11 @@
if not changeAllowed:
if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
- log.debug("Attendee '%s' is creating CANCELLED event for mismatched UID: '%s' - removing entire event" % (self.attendee, self.uid,))
+ log.debug("Attendee '{attendee}' is creating CANCELLED event for mismatched UID: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
returnValue(None)
else:
- log.error("Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s" % (self.attendeePrincipal, self.uid,))
+ log.error("Attendee '{attendee}' is not allowed to make an unauthorized change to an organized event: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1135,21 +1134,21 @@
# Check that the return calendar actually has any components left - this can happen if a cancelled
# component is removed and replaced by another cancelled or invalid one
if self.calendar.mainType() is None:
- log.debug("Attendee '%s' is replacing CANCELLED event: '%s' - removing entire event" % (self.attendee, self.uid,))
+ log.debug("Attendee '{attendee}' is replacing CANCELLED event: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
returnValue(None)
if not doITipReply:
- log.debug("Implicit - attendee '%s' is updating UID: '%s' but change is not significant" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is updating UID: '{uid}' but change is not significant", attendee=self.attendee, uid=self.uid)
returnValue(self.return_calendar)
- log.debug("Attendee '%s' is allowed to update UID: '%s' with local organizer '%s'" % (self.attendee, self.uid, self.organizer))
+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with local organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
elif isinstance(self.organizerAddress, LocalCalendarUser):
# If Organizer copy does not exists we cannot allow SCHEDULE-AGENT=SERVER
if doScheduling:
# Check to see whether all instances are CANCELLED
if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
- log.debug("Attendee '%s' is creating CANCELLED event for missing UID: '%s' - removing entire event" % (self.attendee, self.uid,))
+ log.debug("Attendee '{attendee}' is creating CANCELLED event for missing UID: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT
returnValue(None)
else:
@@ -1157,25 +1156,25 @@
if self.oldcalendar:
oldScheduling = self.oldcalendar.getOrganizerScheduleAgent()
if not oldScheduling:
- log.error("Attendee '%s' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
+ log.error("Attendee '{attendee}' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
"Attendee cannot change organizer state",
))
- log.debug("Attendee '%s' is not allowed to update UID: '%s' - missing organizer copy - removing entire event" % (self.attendee, self.uid,))
+ log.debug("Attendee '{attendee}' is not allowed to update UID: '{uid}' - missing organizer copy - removing entire event", attendee=self.attendee, uid=self.uid)
self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
returnValue(None)
else:
- log.debug("Implicit - attendee '%s' is modifying UID without server scheduling: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is modifying UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
# Nothing else to do
returnValue(None)
elif isinstance(self.organizerAddress, InvalidCalendarUser):
# We will allow the attendee to do anything in this case, but we will mark the organizer
# with an schedule-status error
- log.debug("Attendee '%s' is allowed to update UID: '%s' with invalid organizer '%s'" % (self.attendee, self.uid, self.organizer))
+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with invalid organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
if doScheduling:
self.calendar.setParameterToValueForPropertyWithValue(
"SCHEDULE-STATUS",
@@ -1189,14 +1188,14 @@
# to make any change they like as we cannot verify what is reasonable. In reality
# we ought to be comparing the Attendee changes against the attendee's own copy
# and restrict changes based on that when the organizer's copy is not available.
- log.debug("Attendee '%s' is allowed to update UID: '%s' with remote organizer '%s'" % (self.attendee, self.uid, self.organizer))
+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with remote organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
changedRids = None
if doScheduling:
- log.debug("Implicit - attendee '%s' is updating UID: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is updating UID: '{uid}'", attendee=self.attendee, uid=self.uid)
yield self.scheduleWithOrganizer(changedRids)
else:
- log.debug("Implicit - attendee '%s' is updating UID without server scheduling: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' is updating UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
# Nothing else to do
@@ -1205,7 +1204,7 @@
if self.action == "remove":
# Nothing else to do
- log.debug("Implicit - missing attendee is removing UID without server scheduling: '%s'" % (self.uid,))
+ log.debug("Implicit - missing attendee is removing UID without server scheduling: '{uid}'", uid=self.uid)
else:
# Make sure ORGANIZER is not changed if originally SCHEDULE-AGENT=SERVER
@@ -1214,7 +1213,7 @@
oldOrganizer = self.oldcalendar.getOrganizer()
newOrganizer = self.calendar.getOrganizer()
if oldOrganizer != newOrganizer and self.oldcalendar.getOrganizerScheduleAgent():
- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1223,7 +1222,7 @@
# Never allow a missing attendee with a locally hosted organizer
if isinstance(self.organizerAddress, LocalCalendarUser):
- log.error("Cannot remove ATTENDEE: UID:%s" % (self.uid,))
+ log.error("Cannot remove ATTENDEE: UID:{uid}", uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1232,7 +1231,7 @@
# We will allow the attendee to do anything in this case, but we will mark the organizer
# with an schedule-status error and schedule-agent none
- log.debug("Missing attendee is allowed to update UID: '%s' with invalid organizer '%s'" % (self.uid, self.organizer))
+ log.debug("Missing attendee is allowed to update UID: '{uid}' with invalid organizer '{organizer}'", uid=self.uid, organizer=self.organizer)
# Check SCHEDULE-AGENT and coerce SERVER to NONE
if self.calendar.getOrganizerScheduleAgent():
@@ -1243,14 +1242,14 @@
def checkOrganizerScheduleAgent(self):
is_server = self.calendar.getOrganizerScheduleAgent()
- local_organizer = type(self.organizerAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
+ local_organizer = type(self.organizerAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
if config.Scheduling.iMIP.Enabled and self.organizerAddress.cuaddr.lower().startswith("mailto:"):
return is_server
if not config.Scheduling.iSchedule.Enabled and not local_organizer and is_server:
# Coerce ORGANIZER to SCHEDULE-AGENT=NONE
- log.debug("Attendee '%s' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
+ log.debug("Attendee '{attendee}' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
self.calendar.setParameterToValueForPropertyWithValue("SCHEDULE-AGENT", "NONE", "ORGANIZER", None)
self.calendar.setParameterToValueForPropertyWithValue("SCHEDULE-STATUS", iTIPRequestStatus.NO_USER_SUPPORT_CODE, "ORGANIZER", None)
is_server = False
@@ -1272,8 +1271,8 @@
calendar_resource = (yield getCalendarObjectForRecord(self.calendar_home.transaction(), self.organizerPrincipal, self.uid))
if calendar_resource is not None:
self.organizer_calendar = (yield calendar_resource.componentForUser())
- elif type(self.organizerAddress) in (PartitionedCalendarUser, OtherServerCalendarUser,):
- # For partitioning where the organizer is on a different node, we will assume that the attendee's copy
+ elif type(self.organizerAddress) in (OtherServerCalendarUser,):
+ # For podding where the organizer is on a different node, we will assume that the attendee's copy
# of the event is up to date and "authoritative". So we pretend that is the organizer copy
self.organizer_calendar = self.oldcalendar
@@ -1290,7 +1289,7 @@
oldcalendar = self.organizer_calendar
oldcalendar.attendeesView((self.attendee,), onlyScheduleAgentServer=True)
if oldcalendar.mainType() is None:
- log.debug("Implicit - attendee '%s' cannot use an event they are not an attendee of, UID: '%s'" % (self.attendee, self.uid))
+ log.debug("Implicit - attendee '{attendee}' cannot use an event they are not an attendee of, UID: '{uid}'", attendee=self.attendee, uid=self.uid)
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "valid-attendee-change"),
@@ -1339,7 +1338,7 @@
def _gotResponse(response):
self.handleSchedulingResponse(response, False)
- log.info("Implicit %s - attendee: '%s' to organizer: '%s', UID: '%s'" % (action, self.attendee, self.organizer, self.uid,))
+ log.info("Implicit {action} - attendee: '{attendee}' to organizer: '{organizer}', UID: '{uid}'", action=action, attendee=self.attendee, organizer=self.organizer, uid=self.uid)
d = scheduler.doSchedulingViaPUT(self.originator, (self.organizer,), itipmsg, internal_request=True)
d.addCallback(_gotResponse)
return d
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -40,8 +40,7 @@
from twistedcaldav.ical import normalizeCUAddress, Component
from twistedcaldav.util import utf8String
-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser, RemoteCalendarUser, \
- OtherServerCalendarUser
+from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser, OtherServerCalendarUser
from txdav.caldav.datastore.scheduling.delivery import DeliveryService
from txdav.caldav.datastore.scheduling.ischedule.dkim import DKIMRequest, DKIMUtils
from txdav.caldav.datastore.scheduling.ischedule.remoteservers import IScheduleServerRecord
@@ -58,7 +57,7 @@
"""
Handles the sending of iSchedule scheduling messages. Used for both cross-domain scheduling,
-as well as internal partitioning or podding.
+as well as internal podding.
"""
__all__ = [
@@ -72,6 +71,7 @@
class ScheduleViaISchedule(DeliveryService):
domainServerMap = {}
+ servermgr = None
@classmethod
def serviceType(cls):
@@ -82,9 +82,7 @@
@inlineCallbacks
def matchCalendarUserAddress(cls, cuaddr):
- # TODO: here is where we would attempt service discovery based on the cuaddr.
-
- # Only handle mailtos:
+ # Handle mailtos:
if cuaddr.lower().startswith("mailto:"):
domain = extractEmailDomain(cuaddr)
server = (yield cls.serverForDomain(domain))
@@ -100,25 +98,30 @@
def serverForDomain(cls, domain):
if domain not in cls.domainServerMap:
- # First check built-in list of remote servers
- servermgr = IScheduleServers()
- server = servermgr.mapDomain(domain)
- if server is not None:
- cls.domainServerMap[domain] = server
- else:
- # Lookup domain
- result = (yield lookupServerViaSRV(domain))
- if result is None:
+ if config.Scheduling.iSchedule.Enabled:
+
+ # First check built-in list of remote servers
+ if cls.servermgr is None:
+ cls.servermgr = IScheduleServers()
+ server = cls.servermgr.mapDomain(domain)
+ if server is not None:
+ cls.domainServerMap[domain] = server
+ else:
# Lookup domain
- result = (yield lookupServerViaSRV(domain, service="_ischedule"))
+ result = (yield lookupServerViaSRV(domain))
if result is None:
- cls.domainServerMap[domain] = None
+ # Lookup domain
+ result = (yield lookupServerViaSRV(domain, service="_ischedule"))
+ if result is None:
+ cls.domainServerMap[domain] = None
+ else:
+ # Create the iSchedule server record for this server
+ cls.domainServerMap[domain] = IScheduleServerRecord(uri="http://%s:%s/.well-known/ischedule" % result)
else:
# Create the iSchedule server record for this server
- cls.domainServerMap[domain] = IScheduleServerRecord(uri="http://%s:%s/.well-known/ischedule" % result)
- else:
- # Create the iSchedule server record for this server
- cls.domainServerMap[domain] = IScheduleServerRecord(uri="https://%s:%s/.well-known/ischedule" % result)
+ cls.domainServerMap[domain] = IScheduleServerRecord(uri="https://%s:%s/.well-known/ischedule" % result)
+ else:
+ cls.domainServerMap[domain] = None
returnValue(cls.domainServerMap[domain])
@@ -136,8 +139,6 @@
if isinstance(recipient, RemoteCalendarUser):
# Map the recipient's domain to a server
server = (yield self.serverForDomain(recipient.domain))
- elif isinstance(recipient, PartitionedCalendarUser):
- server = self._getServerForPartitionedUser(recipient)
elif isinstance(recipient, OtherServerCalendarUser):
server = self._getServerForOtherServerUser(recipient)
else:
@@ -182,20 +183,6 @@
yield DeferredList(deferreds)
- def _getServerForPartitionedUser(self, recipient):
-
- if not hasattr(self, "partitionedServers"):
- self.partitionedServers = {}
-
- partition = recipient.principal.partitionURI()
- if partition not in self.partitionedServers:
- self.partitionedServers[partition] = IScheduleServerRecord(uri=joinURL(partition, "/ischedule"))
- self.partitionedServers[partition].unNormalizeAddresses = False
- self.partitionedServers[partition].moreHeaders.append(recipient.principal.server().secretHeader())
-
- return self.partitionedServers[partition]
-
-
def _getServerForOtherServerUser(self, recipient):
if not hasattr(self, "otherServers"):
@@ -203,9 +190,12 @@
serverURI = recipient.principal.serverURI()
if serverURI not in self.otherServers:
- self.otherServers[serverURI] = IScheduleServerRecord(uri=joinURL(serverURI, "/ischedule"))
- self.otherServers[serverURI].unNormalizeAddresses = not recipient.principal.server().isImplicit
- self.otherServers[serverURI].moreHeaders.append(recipient.principal.server().secretHeader())
+ self.otherServers[serverURI] = IScheduleServerRecord(
+ uri=joinURL(serverURI, config.Servers.InboxName),
+ unNormalizeAddresses=not recipient.principal.server().isImplicit,
+ moreHeaders=[recipient.principal.server().secretHeader(), ],
+ podding=True,
+ )
return self.otherServers[serverURI]
@@ -222,6 +212,7 @@
self.refreshOnly = refreshOnly
self.headers = None
self.data = None
+ self.original_organizer = None
@inlineCallbacks
@@ -365,7 +356,8 @@
# The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
- originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
+ if self.server.unNormalizeAddresses:
+ originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
self.headers.addRawHeader("Originator", utf8String(originator))
self.sign_headers.append("Originator")
@@ -414,15 +406,15 @@
"""
if self.data is None:
+
# Need to remap cuaddrs from urn:uuid
- if self.server.unNormalizeAddresses and self.scheduler.method == "PUT":
- normalizedCalendar = self.scheduler.calendar.duplicate()
+ normalizedCalendar = self.scheduler.calendar.duplicate()
+ self.original_organizer = normalizedCalendar.getOrganizer()
+ if self.server.unNormalizeAddresses:
normalizedCalendar.normalizeCalendarUserAddresses(
normalizationLookup,
self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
toUUID=False)
- else:
- normalizedCalendar = self.scheduler.calendar
# For VFREEBUSY we need to strip out ATTENDEEs that do not match the recipient list
if self.scheduler.isfreebusy:
@@ -445,13 +437,12 @@
f = Factory()
f.protocol = HTTPClientProtocol
if ssl:
- ep = GAIEndpoint(reactor, host, port,
- _configuredClientContextFactory())
+ ep = GAIEndpoint(reactor, host, port, _configuredClientContextFactory())
else:
ep = GAIEndpoint(reactor, host, port)
proto = (yield ep.connect(f))
- if config.Scheduling.iSchedule.DKIM.Enabled:
+ if not self.server.podding() and config.Scheduling.iSchedule.DKIM.Enabled:
domain, selector, key_file, algorithm, useDNSKey, useHTTPKey, usePrivateExchangeKey, expire = DKIMUtils.getConfiguration(config)
request = DKIMRequest(
"POST",
@@ -503,6 +494,14 @@
calendar_data = response.childOfType(CalendarData)
if calendar_data:
calendar_data = str(calendar_data)
+ if self.server.unNormalizeAddresses and self.original_organizer is not None:
+ # Need to restore original ORGANIZER value if it got unnormalized
+ calendar = Component.fromString(calendar_data)
+ organizers = calendar.getAllPropertiesInAnyComponent("ORGANIZER", depth=1)
+ for organizer in organizers:
+ organizer.setValue(self.original_organizer)
+ calendar_data = str(calendar)
+
error = response.childOfType(Error)
if error:
error = error.children
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -31,18 +31,12 @@
the principals across the whole domain need to be able to directly schedule each other and know of each others
existence. A common scenario would be a production server and a development/test server.
-Each server is identified by an id and url. The id is used when assigning principals to a specific server. Each
-server can also support multiple partitions, and each of those is identified by an id and url, with the id also
-being used to assign principals to a specific partition.
+Each server is identified by an id and url. The id is used when assigning principals to a specific server.
-These servers support the concept of "partitioning" and "podding".
+These servers support the concept of "podding".
-A "partitioned" service is one that spreads its
-users out across multiple stores and does reverse proxying of incoming requests to the appropriate partitioned host.
-All servers within the same partition have to be running the same version of the software etc.
-
A "podded" service is one where different groups of users are hosted on different servers, which may be of
-different versions etc. A "pod" may itself be "partitioned", but the partitioning is "invisible" to the outside world.
+different versions etc.
"""
__all__ = [
@@ -104,13 +98,31 @@
def getThisServer(self):
return self._thisServer
+
+ def installReverseProxies(self, maxClients):
+ """
+ Install a reverse proxy for each of the other servers in the "pod".
+
+ @param maxClients: maximum number of clients in the pool.
+ @type maxClients: C{int}
+ """
+
+ for server in self._servers.values():
+ if server.thisServer:
+ continue
+ installPool(
+ server.id,
+ server.uri,
+ maxClients,
+ )
+
Servers = ServersDB() # Global server DB
class Server(object):
"""
- Represents a server which may itself be partitioned.
+ Represents a server.
"""
def __init__(self):
@@ -120,8 +132,6 @@
self.ips = set()
self.allowed_from_ips = set()
self.shared_secret = None
- self.partitions = {}
- self.partitions_ips = set()
self.isImplicit = True
@@ -164,25 +174,12 @@
actual_ips.add(item)
self.allowed_from_ips = actual_ips
- for uri in self.partitions.values():
- parsed_uri = urlparse.urlparse(uri)
- try:
- ips = getIPsFromHost(parsed_uri.hostname)
- except socket.gaierror, e:
- msg = "Unable to lookup ip-addr for partition '%s': %s" % (parsed_uri.hostname, str(e))
- log.error(msg)
- if ignoreIPLookupFailures:
- ips = ()
- else:
- raise ValueError(msg)
- self.partitions_ips.update(ips)
-
def checkThisIP(self, ip):
"""
- Check that the passed in IP address corresponds to this server or one of its partitions.
+ Check that the passed in IP address corresponds to this server.
"""
- return (ip in self.ips) or (ip in self.partitions_ips)
+ return (ip in self.ips)
def hasAllowedFromIP(self):
@@ -218,38 +215,13 @@
return (SERVER_SECRET_HEADER, self.shared_secret,)
- def addPartition(self, id, uri):
- self.partitions[id] = uri
-
- def getPartitionURIForId(self, id):
- return self.partitions.get(id)
-
-
- def isPartitioned(self):
- return len(self.partitions) != 0
-
-
- def installReverseProxies(self, ownUID, maxClients):
-
- for partition, url in self.partitions.iteritems():
- if partition != ownUID:
- installPool(
- partition,
- url,
- maxClients,
- )
-
-
-
ELEMENT_SERVERS = "servers"
ELEMENT_SERVER = "server"
ELEMENT_ID = "id"
ELEMENT_URI = "uri"
ELEMENT_ALLOWED_FROM = "allowed-from"
ELEMENT_SHARED_SECRET = "shared-secret"
-ELEMENT_PARTITIONS = "partitions"
-ELEMENT_PARTITION = "partition"
ATTR_IMPLICIT = "implicit"
ATTR_VALUE_YES = "yes"
ATTR_VALUE_NO = "no"
@@ -286,39 +258,13 @@
server.allowed_from_ips.add(node.text)
elif node.tag == ELEMENT_SHARED_SECRET:
server.shared_secret = node.text
- elif node.tag == ELEMENT_PARTITIONS:
- ServersParser._parsePartition(xmlFile, node, server)
else:
raise RuntimeError("Invalid element '%s' in servers file: '%s'" % (node.tag, xmlFile,))
if server.id is None or server.uri is None:
- raise RuntimeError("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,))
+ raise RuntimeError("Invalid server '%s' in servers file: '%s'" % (child.tag, xmlFile,))
server.check(ignoreIPLookupFailures=ignoreIPLookupFailures)
results[server.id] = server
return results
-
-
- @staticmethod
- def _parsePartition(xmlFile, partitions, server):
-
- for child in partitions:
-
- if child.tag != ELEMENT_PARTITION:
- raise RuntimeError("Unknown partition type: '%s' in servers file: '%s'" % (child.tag, xmlFile,))
-
- id = None
- uri = None
- for node in child:
- if node.tag == ELEMENT_ID:
- id = node.text
- elif node.tag == ELEMENT_URI:
- uri = node.text
- else:
- raise RuntimeError("Invalid element '%s' in augment file: '%s'" % (node.tag, xmlFile,))
-
- if id is None or uri is None:
- raise RuntimeError("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,))
-
- server.addPartition(id, uri)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -23,7 +23,7 @@
"""
XML based iSchedule configuration file handling. This is for handling of remote servers. The localservers.py module
-handles servers that are local (partitioned or podded).
+handles servers that are local (podded).
"""
__all__ = [
@@ -138,7 +138,7 @@
"""
Contains server-to-server details.
"""
- def __init__(self, uri=None):
+ def __init__(self, uri=None, unNormalizeAddresses=True, moreHeaders=[], podding=False):
"""
@param recordType: record type for directory entry.
"""
@@ -148,8 +148,9 @@
self.allow_to = True
self.domains = []
self.client_hosts = []
- self.unNormalizeAddresses = True
- self.moreHeaders = []
+ self.unNormalizeAddresses = unNormalizeAddresses
+ self.moreHeaders = moreHeaders
+ self._podding = podding
if uri:
self.uri = uri
@@ -160,6 +161,10 @@
return (self.ssl, self.host, self.port, self.path,)
+ def podding(self):
+ return self._podding
+
+
def redirect(self, location):
"""
Permanent redirect for the lifetime of this record.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,8 +14,8 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twext.web2 import responsecode
from twext.web2.dav.http import ErrorResponse
@@ -52,7 +52,7 @@
Extends L{DAVResource} to provide iSchedule inbox functionality.
"""
- def __init__(self, parent, store):
+ def __init__(self, parent, store, podding=False):
"""
@param parent: the parent resource of this one.
"""
@@ -62,6 +62,7 @@
self.parent = parent
self._newStore = store
+ self._podding = podding
def deadProperties(self):
@@ -109,12 +110,12 @@
def render(self, request):
output = """<html>
<head>
-<title>Server To Server Inbox Resource</title>
+<title>%(rtype)s Inbox Resource</title>
</head>
<body>
-<h1>Server To Server Inbox Resource.</h1>
+<h1>%(rtype)s Inbox Resource.</h1>
</body
-</html>"""
+</html>""" % {"rtype" : "Podding" if self._podding else "iSchedule", }
response = Response(200, {}, output)
response.headers.setHeader("content-type", MimeType("text", "html"))
@@ -126,7 +127,7 @@
The iSchedule GET method.
"""
- if not request.args:
+ if not request.args or self._podding:
# Do normal GET behavior
return self.render(request)
@@ -157,12 +158,26 @@
"""
# Determine min/max date-time for iSchedule
- now = PyCalendarDateTime.getNowUTC()
- minDateTime = PyCalendarDateTime(now.getYear(), 1, 1, 0, 0, 0, PyCalendarTimezone(utc=True))
+ now = DateTime.getNowUTC()
+ minDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone(utc=True))
minDateTime.offsetYear(-1)
- maxDateTime = PyCalendarDateTime(now.getYear(), 1, 1, 0, 0, 0, PyCalendarTimezone(utc=True))
+ maxDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone(utc=True))
maxDateTime.offsetYear(10)
+ dataTypes = []
+ dataTypes.append(
+ ischedulexml.CalendarDataType(**{
+ "content-type": "text/calendar",
+ "version": "2.0",
+ })
+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ ischedulexml.CalendarDataType(**{
+ "content-type": "application/calendar+json",
+ "version": "2.0",
+ })
+ )
result = ischedulexml.QueryResult(
ischedulexml.Capabilities(
@@ -188,12 +203,7 @@
name="VFREEBUSY"
),
),
- ischedulexml.CalendarDataTypes(
- ischedulexml.CalendarDataType(**{
- "content-type": "text/calendar",
- "version": "2.0",
- }),
- ),
+ ischedulexml.CalendarDataTypes(*dataTypes),
ischedulexml.Attachments(
ischedulexml.External(),
),
@@ -220,26 +230,51 @@
txn = transactionFromRequest(request, self._newStore)
# This is a server-to-server scheduling operation.
- scheduler = IScheduleScheduler(txn, None)
+ scheduler = IScheduleScheduler(txn, None, podding=self._podding)
+ # Check content first
+ contentType = request.headers.getHeader("content-type")
+ format = self.determineType(contentType)
+
+ if format is None:
+ msg = "MIME type %s not allowed in iSchedule request" % (contentType,)
+ self.log.error(msg)
+ raise HTTPError(scheduler.errorResponse(
+ responsecode.FORBIDDEN,
+ (ischedule_namespace, "invalid-calendar-data-type"),
+ msg,
+ ))
+
originator = self.loadOriginatorFromRequestHeaders(request)
recipients = self.loadRecipientsFromRequestHeaders(request)
body = (yield allDataFromStream(request.stream))
+ calendar = Component.fromString(body, format=format)
# Do the POST processing treating this as a non-local schedule
try:
- result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, originator, recipients))
+ result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, calendar, originator, recipients))
except Exception:
ex = Failure()
yield txn.abort()
ex.raiseException()
else:
yield txn.commit()
- response = result.response()
- response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
+ response = result.response(format=format)
+ if not self._podding:
+ response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
returnValue(response)
+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in Component.allowedTypes() else None
+
+
def loadOriginatorFromRequestHeaders(self, request):
# Must have Originator header
originator = request.headers.getRawHeaders("originator")
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -23,7 +23,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.config import config
-from twistedcaldav.ical import normalizeCUAddress, Component
+from twistedcaldav.ical import normalizeCUAddress
from txdav.caldav.datastore.scheduling import addressmapping
from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
@@ -119,6 +119,9 @@
class IScheduleScheduler(RemoteScheduler):
+ """
+ Handles iSchedule and podding requests.
+ """
scheduleResponse = IScheduleResponseQueue
@@ -138,8 +141,13 @@
"max-recipients": (ischedule_namespace, "max-recipients"),
}
+ def __init__(self, txn, originator_uid, logItems=None, noAttendeeRefresh=False, podding=False):
+ super(IScheduleScheduler, self).__init__(txn, originator_uid, logItems=logItems, noAttendeeRefresh=noAttendeeRefresh)
+ self._podding = podding
+
+
@inlineCallbacks
- def doSchedulingViaPOST(self, remoteAddr, headers, body, originator, recipients):
+ def doSchedulingViaPOST(self, remoteAddr, headers, body, calendar, originator, recipients):
"""
Carry out iSchedule specific processing.
"""
@@ -148,7 +156,7 @@
self.headers = headers
self.verified = False
- if config.Scheduling.iSchedule.DKIM.Enabled:
+ if not self._podding and config.Scheduling.iSchedule.DKIM.Enabled:
verifier = DKIMVerifier(self.headers, body, protocol_debug=config.Scheduling.iSchedule.DKIM.ProtocolDebug)
try:
yield verifier.verify()
@@ -172,13 +180,16 @@
msg,
))
- calendar = Component.fromString(body)
-
- if self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
+ if self._podding and self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
self.txn.doing_attendee_refresh = 1
# Normalize recipient addresses
- recipients = [normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress) for recipient in recipients]
+ results = []
+ for recipient in recipients:
+ normalized = normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
+ self.recipientsNormalizationMap[normalized] = recipient
+ results.append(normalized)
+ recipients = results
result = (yield super(IScheduleScheduler, self).doSchedulingViaPOST(originator, recipients, calendar))
returnValue(result)
@@ -218,7 +229,7 @@
originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
if originatorPrincipal or localUser:
- if originatorPrincipal.locallyHosted():
+ if originatorPrincipal.thisServer():
log.error("Cannot use originator that is on this server: %s" % (self.originator,))
raise HTTPError(self.errorResponse(
responsecode.FORBIDDEN,
@@ -296,23 +307,17 @@
def _validAlternateServer(self, principal):
"""
- Check the validity of the partitioned host.
+ Check the validity of the podded host.
"""
- # Extract expected host/port. This will be the partitionURI, or if no partitions,
- # the serverURI
- expected_uri = principal.partitionURI()
- if expected_uri is None:
- expected_uri = principal.serverURI()
+ # Extract expected host/port. This will be the serverURI.
+ expected_uri = principal.serverURI()
expected_uri = urlparse.urlparse(expected_uri)
# Get the request IP and map to hostname.
clientip = self.remoteAddr.host
- # Check against this server (or any of its partitions). We need this because an external iTIP message
- # may be addressed to users on different partitions, and the node receiving the iTIP message will need to
- # forward it to the partition nodes, thus the client ip seen by the partitions will in fact be the initial
- # receiving node.
+ # Check against this server.
matched = False
if Servers.getThisServer().checkThisIP(clientip):
matched = True
@@ -364,7 +369,7 @@
if organizer:
organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
if organizerPrincipal:
- if organizerPrincipal.locallyHosted():
+ if organizerPrincipal.thisServer():
log.error("Invalid ORGANIZER in calendar data: %s" % (self.calendar,))
raise HTTPError(self.errorResponse(
responsecode.FORBIDDEN,
@@ -372,7 +377,7 @@
"Organizer is not local to server",
))
else:
- # Check that the origin server is the correct partition
+ # Check that the origin server is the correct pod
self.organizer = calendarUserFromPrincipal(organizer, organizerPrincipal)
self._validAlternateServer(self.organizer.principal)
else:
@@ -405,7 +410,7 @@
# Attendee cannot be local.
attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
if attendeePrincipal:
- if attendeePrincipal.locallyHosted():
+ if attendeePrincipal.thisServer():
log.error("Invalid ATTENDEE in calendar data: %s" % (self.calendar,))
raise HTTPError(self.errorResponse(
responsecode.FORBIDDEN,
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,6 +42,7 @@
Make sure we do an exact comparison on EmailDomain
"""
+ self.patch(config.Scheduling.iSchedule, "Enabled", True)
self.patch(config.Scheduling.iSchedule, "RemoteServers", "")
# Only mailtos:
@@ -64,3 +65,9 @@
self.assertFalse(result)
result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user")
self.assertFalse(result)
+
+ # Test when not enabled
+ ScheduleViaISchedule.domainServerMap = {}
+ self.patch(config.Scheduling.iSchedule, "Enabled", False)
+ result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user at example.com")
+ self.assertFalse(result)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -37,16 +37,6 @@
<server>
<id>00002</id>
<uri>https://caldav2.example.com:8843</uri>
- <partitions>
- <partition>
- <id>A</id>
- <uri>https://machine1.example.com:8443</uri>
- </partition>
- <partition>
- <id>B</id>
- <uri>https://machine2.example.com:8443</uri>
- </partition>
- </partitions>
</server>
</servers>
"""
@@ -62,16 +52,6 @@
<server>
<id>00002</id>
<uri>https://caldav2.example.com:8843</uri>
- <partitions>
- <partition>
- <id>A</id>
- <uri>https://machine1.example.com:8443</uri>
- </partition>
- <partition>
- <id>B</id>
- <uri>https://machine2.example.com:8443</uri>
- </partition>
- </partitions>
</server>
</servers>
"""
@@ -103,13 +83,7 @@
self.assertEqual(servers.getServerById("00001").shared_secret, "foobar")
self.assertEqual(servers.getServerById("00002").shared_secret, None)
- self.assertEqual(len(servers.getServerById("00001").partitions), 0)
- self.assertEqual(len(servers.getServerById("00002").partitions), 2)
- self.assertEqual(servers.getServerById("00002").getPartitionURIForId("A"), "https://machine1.example.com:8443")
- self.assertEqual(servers.getServerById("00002").getPartitionURIForId("B"), "https://machine2.example.com:8443")
-
-
def test_this_server(self):
servers = self._setupServers()
@@ -129,14 +103,6 @@
self.assertTrue(servers.getServerById("00002").thisServer)
- def test_check_is_partitioned(self):
-
- servers = self._setupServers()
-
- self.assertFalse(servers.getServerById("00001").isPartitioned())
- self.assertTrue(servers.getServerById("00002").isPartitioned())
-
-
def test_check_this_ip(self):
servers = self._setupServers()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -94,6 +94,7 @@
headers=http_headers.Headers(rawHeaders={
"Originator": ("mailto:wsanchez at example.com",),
"Recipient": ("mailto:cdaboo at example.com",),
+ "Content-Type": "text/calendar",
}),
content="""BEGIN:VCALENDAR
CALSCALE:GREGORIAN
@@ -144,6 +145,7 @@
headers=http_headers.Headers(rawHeaders={
"Originator": ("mailto:user01 at example.org",),
"Recipient": ("mailto:user02 at example.com",),
+ "Content-Type": ("text/calendar",)
}),
content="""BEGIN:VCALENDAR
CALSCALE:GREGORIAN
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -33,7 +33,7 @@
from twistedcaldav.ical import Property, iCalendarProductID, Component, \
ignoredComponents
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
log = Logger()
@@ -402,9 +402,14 @@
@staticmethod
def updateAttendeeData(from_component, to_component):
"""
+ Called when processing a REPLY only.
+
Copy the PARTSTAT of the Attendee in the from_component to the matching ATTENDEE
in the to_component. Ignore if no match found. Also update the private comments.
+ For VPOLL we need to copy POLL-ITEM-ID response values into the actual matching
+ polled sub-components as VOTER properties.
+
@param from_component: component to copy from
@type from_component: L{Component}
@param to_component: component to copy to
@@ -423,7 +428,7 @@
reqstatus = "2.0"
# Get attendee in from_component - there MUST be only one
- attendees = tuple(from_component.properties("ATTENDEE"))
+ attendees = tuple(from_component.properties(from_component.recipientPropertyName()))
if len(attendees) != 1:
log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),))
return None, False, False
@@ -471,16 +476,9 @@
pass
elif attendee_comment is None and private_comment is not None:
- # Remove all property parameters
- private_comment.removeAllParameters()
+ # We now remove the private comment on the organizer's side if the attendee removed it
+ to_component.removeProperty(private_comment)
- # Add default parameters
- private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
-
- # Set value empty
- private_comment.setValue("")
-
private_comment_changed = True
elif attendee_comment is not None and private_comment is None:
@@ -491,7 +489,7 @@
attendee_comment.value(),
params={
"X-CALENDARSERVER-ATTENDEE-REF": attendee.value(),
- "X-CALENDARSERVER-DTSTAMP": PyCalendarDateTime.getNowUTC().getText(),
+ "X-CALENDARSERVER-DTSTAMP": DateTime.getNowUTC().getText(),
}
)
to_component.addProperty(private_comment)
@@ -506,17 +504,60 @@
# Add default parameters
private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
+ private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
# Set new value
private_comment.setValue(attendee_comment.value())
private_comment_changed = True
+ # Do VPOLL transfer
+ if from_component.name() == "VPOLL":
+ # TODO: figure out how to report changes back
+ iTipProcessing.updateVPOLLData(from_component, to_component, attendee)
+
return attendee.value(), partstat_changed, private_comment_changed
@staticmethod
+ def updateVPOLLData(from_component, to_component, attendee):
+ """
+ Update VPOLL sub-components with voter's response.
+
+ @param from_component: component to copy from
+ @type from_component: L{Component}
+ @param to_component: component to copy to
+ @type to_component: L{Component}
+ @param attendee: attendee being processed
+ @type attendee: L{Property}
+ """
+
+ responses = {}
+ for prop in from_component.properties("POLL-ITEM-ID"):
+ responses[prop.value()] = prop
+
+ for component in to_component.subcomponents():
+ if component.name() in ignoredComponents:
+ continue
+ poll_item_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_item_id is None:
+ continue
+ voter = component.getVoterProperty((attendee.value(),))
+
+ # If no response - remove
+ if poll_item_id not in responses or not responses[poll_item_id].hasParameter("RESPONSE"):
+ if voter is not None:
+ component.removeProperty(voter)
+ continue
+
+ # Add or update voter
+ if voter is None:
+ voter = Property("VOTER", attendee.value())
+ component.addProperty(voter)
+ voter.setParameter("RESPONSE", responses[poll_item_id].parameterValue("RESPONSE"))
+
+
+ @staticmethod
def transferItems(from_calendar, to_component, master_valarms, private_comments, transps, completeds, organizer_schedule_status, attendee_dtstamp, other_props, recipient, remove_matched=False):
"""
Transfer properties from a calendar to a component by first trying to match the component in the original calendar and
@@ -854,7 +895,7 @@
itip.filterComponents(changedRids)
# Force update to DTSTAMP everywhere so reply sequencing will work
- itip.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+ itip.replacePropertyInAllComponents(Property("DTSTAMP", DateTime.getNowUTC()))
# Remove all attendees except the one we want
itip.removeAllButOneAttendee(attendee)
@@ -884,6 +925,7 @@
"EXDATE",
"ORGANIZER",
"ATTENDEE",
+ "VOTER",
"X-CALENDARSERVER-PRIVATE-COMMENT",
"SUMMARY",
"LOCATION",
@@ -903,10 +945,38 @@
# Strip out unwanted bits
iTipGenerator.prepareSchedulingMessage(itip, reply=True)
+ # Handle VPOLL behavior
+ for component in itip.subcomponents():
+ if component.name() == "VPOLL":
+ iTipGenerator.generateVPOLLReply(component, attendee)
+
return itip
@staticmethod
+ def generateVPOLLReply(vpoll, attendee):
+ """
+ Generate the proper poll response in a reply for each component being voted on.
+
+ @param vpoll: the VPOLL component to process
+ @type vpoll: L{Component}
+ @param attendee: calendar user address of attendee replying
+ @type attendee: C{str}
+ """
+
+ for component in tuple(vpoll.subcomponents()):
+ if component.name() in ignoredComponents:
+ continue
+ poll_item_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_item_id is None:
+ continue
+ voter = component.getVoterProperty((attendee,))
+ if voter is not None and voter.hasParameter("RESPONSE"):
+ vpoll.addProperty(Property("POLL-ITEM-ID", poll_item_id, {"RESPONSE": voter.parameterValue("RESPONSE")}))
+ vpoll.removeComponent(component)
+
+
+ @staticmethod
def prepareSchedulingMessage(itip, reply=False):
"""
Remove properties and parameters that should not be sent in an iTIP message
@@ -932,6 +1002,7 @@
# Property Parameters
itip.removePropertyParameters("ATTENDEE", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
+ itip.removePropertyParameters("VOTER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
itip.removePropertyParameters("ORGANIZER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,9 +14,9 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
from twext.python.log import Logger
from twext.web2.dav.method.report import NumberOfMatchesWithinLimits
@@ -785,13 +785,13 @@
cuas = self.recipient.principal.calendarUserAddresses
# First expand current one to get instances (only go 1 year into the future)
- default_future_expansion_duration = PyCalendarDuration(days=config.Scheduling.Options.AutoSchedule.FutureFreeBusyDays)
- expand_max = PyCalendarDateTime.getToday() + default_future_expansion_duration
+ default_future_expansion_duration = Duration(days=config.Scheduling.Options.AutoSchedule.FutureFreeBusyDays)
+ expand_max = DateTime.getToday() + default_future_expansion_duration
instances = calendar.expandTimeRanges(expand_max, ignoreInvalidInstances=True)
# We are going to ignore auto-accept processing for anything more than a day old (actually use -2 days
# to add some slop to account for possible timezone offsets)
- min_date = PyCalendarDateTime.getToday()
+ min_date = DateTime.getToday()
min_date.offsetDay(-2)
allOld = True
@@ -824,7 +824,7 @@
# Get the timezone property from the collection, and store in the query filter
# for use during the query itself.
tz = testcal.getTimezone()
- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
# Now do search for overlapping time-range and set instance.free based
# on whether there is an overlap or not
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -38,11 +38,12 @@
from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
from txdav.caldav.datastore.scheduling.cuaddress import EmailCalendarUser
-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser
from txdav.caldav.datastore.scheduling.imip.delivery import ScheduleViaIMip
from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule
from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
+
import hashlib
+from collections import namedtuple
"""
CalDAV/Server-to-Server scheduling behavior.
@@ -50,8 +51,8 @@
This module handles the delivery of scheduling messages to organizer and attendees. The basic idea is to first
confirm the integrity of the incoming scheduling message, check authorization. Appropriate L{DeliveryService}s
are then used to deliver the message to attendees or organizer. Delivery responses are processed and returned.
-This takes into account partitioning and podding of users by detecting the appropriate host for a calendar
-user and then dispatching the delivery accordingly.
+This takes into account podding of users by detecting the appropriate host for a calendar user and then
+dispatching the delivery accordingly.
The L{Scheduler} class defines the basic behavior for processing deliveries. Sub-classes are defined for the
different ways a deliver can be triggered.
@@ -142,6 +143,7 @@
self.originator = None
self.recipients = None
+ self.recipientsNormalizationMap = {}
self.calendar = None
self.organizer = None
self.attendee = None
@@ -232,51 +234,6 @@
returnValue(result)
- @inlineCallbacks
- def loadFromRequestData(self):
- self.loadOriginatorFromRequestDetails()
- self.loadRecipientsFromCalendarData()
-
-
- def loadOriginatorFromRequestDetails(self):
- # Get the originator who is the authenticated user
- originatorPrincipal = self.txn.directoryService().recordWithUID(self.originator_uid)
-
- # Pick the canonical CUA:
- originator = originatorPrincipal.canonicalCalendarUserAddress() if originatorPrincipal else ""
-
- if not originator:
- log.error("%s request must have Originator" % (self.method,))
- raise HTTPError(self.errorResponse(
- responsecode.FORBIDDEN,
- self.errorElements["originator-missing"],
- "Missing originator",
- ))
- else:
- self.originator = originator
-
-
- def loadRecipientsFromCalendarData(self):
-
- # Get the ATTENDEEs
- attendees = list()
- unique_set = set()
- for attendee, _ignore in self.calendar.getAttendeesByInstance():
- if attendee not in unique_set:
- attendees.append(attendee)
- unique_set.add(attendee)
-
- if not attendees:
- log.error("%s request must have at least one Recipient" % (self.method,))
- raise HTTPError(self.errorResponse(
- responsecode.FORBIDDEN,
- self.errorElements["recipient-missing"],
- "Must have recipients",
- ))
- else:
- self.recipients = list(attendees)
-
-
def preProcessCalendarData(self):
"""
After loading calendar data from the request, do some optional processing of it. This method will be
@@ -474,11 +431,10 @@
freebusy = self.checkForFreeBusy()
# Prepare for multiple responses
- responses = self.scheduleResponse(self.method, responsecode.OK)
+ responses = self.scheduleResponse(self.method, responsecode.OK, self.mapRecipientAddress)
# Loop over each recipient and aggregate into lists by service types.
caldav_recipients = []
- partitioned_recipients = []
otherserver_recipients = []
remote_recipients = []
imip_recipients = []
@@ -500,9 +456,6 @@
elif isinstance(recipient, LocalCalendarUser):
caldav_recipients.append(recipient)
- elif isinstance(recipient, PartitionedCalendarUser):
- partitioned_recipients.append(recipient)
-
elif isinstance(recipient, OtherServerCalendarUser):
otherserver_recipients.append(recipient)
@@ -524,10 +477,6 @@
if caldav_recipients:
yield self.generateLocalSchedulingResponses(caldav_recipients, responses, freebusy)
- # Now process partitioned recipients
- if partitioned_recipients:
- yield self.generateRemoteSchedulingResponses(partitioned_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
-
# Now process other server recipients
if otherserver_recipients:
yield self.generateRemoteSchedulingResponses(otherserver_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
@@ -577,7 +526,11 @@
return requestor.generateSchedulingResponses()
+ def mapRecipientAddress(self, cuaddr):
+ return self.recipientsNormalizationMap.get(cuaddr, cuaddr)
+
+
class RemoteScheduler(Scheduler):
def checkOrganizer(self):
@@ -612,8 +565,8 @@
else:
# Map recipient to their inbox
inbox = None
- if principal.calendarsEnabled() and principal.thisServer():
- if principal.locallyHosted():
+ if principal.calendarsEnabled():
+ if principal.thisServer():
recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
if recipient_home:
inbox = (yield recipient_home.calendarWithName("inbox"))
@@ -703,7 +656,12 @@
response_description_element = davxml.ResponseDescription
calendar_data_element = caldavxml.CalendarData
- def __init__(self, method, success_response):
+ ScheduleResonseDetails = namedtuple(
+ "ScheduleResonseDetails",
+ ["recipient", "reqstatus", "calendar", "error", "message", ]
+ )
+
+ def __init__(self, method, success_response, recipient_mapper=None):
"""
@param method: the name of the method generating the queue.
@param success_response: the response to return in lieu of a
@@ -712,6 +670,7 @@
self.responses = []
self.method = method
self.success_response = success_response
+ self.recipient_mapper = recipient_mapper
self.location = None
@@ -745,19 +704,20 @@
else:
raise AssertionError("Unknown data type: %r" % (what,))
+ if self.recipient_mapper is not None:
+ recipient = self.recipient_mapper(recipient)
+
if not suppressErrorLog and code > 400: # Error codes only
self.log.error("Error during %s for %s: %s" % (self.method, recipient, message))
- children = []
- children.append(self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient))
- children.append(self.request_status_element(reqstatus))
- if calendar is not None:
- children.append(self.calendar_data_element.fromCalendar(calendar))
- if error is not None:
- children.append(error)
- if message is not None:
- children.append(self.response_description_element(message))
- self.responses.append(self.response_element(*children))
+ details = ScheduleResponseQueue.ScheduleResonseDetails(
+ self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient),
+ self.request_status_element(reqstatus),
+ calendar,
+ error,
+ self.response_description_element(message) if message is not None else None,
+ )
+ self.responses.append(details)
def errorForFailure(self, failure):
@@ -773,19 +733,17 @@
@param clone: the response to clone.
"""
- children = []
- children.append(self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient))
- children.append(self.request_status_element.fromString(request_status))
- if calendar_data is not None:
- children.append(self.calendar_data_element.fromCalendar(calendar_data))
- if error is not None:
- children.append(self.error_element(*error))
- if desc is not None:
- children.append(self.response_description_element.fromString(desc))
- self.responses.append(self.response_element(*children))
+ details = ScheduleResponseQueue.ScheduleResonseDetails(
+ self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient),
+ self.request_status_element.fromString(request_status),
+ calendar_data,
+ self.error_element(*error) if error is not None else None,
+ self.response_description_element.fromString(desc) if desc is not None else None,
+ )
+ self.responses.append(details)
- def response(self):
+ def response(self, format=None):
"""
Generate a L{ScheduleResponseResponse} with the responses contained in the
queue or, if no such responses, return the C{success_response} provided
@@ -793,6 +751,20 @@
@return: the response.
"""
if self.responses:
- return ScheduleResponseResponse(self.schedule_response_element, self.responses, self.location)
+ # Convert our queue to all XML elements
+ xml_responses = []
+ for response in self.responses:
+ children = []
+ children.append(response.recipient)
+ children.append(response.reqstatus)
+ if response.calendar is not None:
+ children.append(self.calendar_data_element.fromCalendar(response.calendar, format))
+ if response.error is not None:
+ children.append(response.error)
+ if response.message is not None:
+ children.append(response.message)
+ xml_responses.append(self.response_element(*children))
+
+ return ScheduleResponseResponse(self.schedule_response_element, xml_responses, self.location)
else:
return self.success_response
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,8 +14,8 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
from twext.python.clsprop import classproperty
@@ -94,7 +94,7 @@
(
"#1.3 With single busy time",
[
- [PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"), ],
+ [Period.parseText("20080601T120000Z/20080601T130000Z"), ],
[],
[],
],
@@ -120,8 +120,8 @@
"#1.4 With multiple busy time",
[
[
- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
],
[],
[],
@@ -148,10 +148,10 @@
"#1.5 With multiple busy time, some overlap",
[
[
- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T123000Z/20080601T133000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
- PyCalendarPeriod.parseText("20080601T150000Z/20080601T160000Z"),
+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T123000Z/20080601T133000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
+ Period.parseText("20080601T150000Z/20080601T160000Z"),
],
[],
[],
@@ -178,14 +178,14 @@
"#1.6 With all busy time types",
[
[
- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
],
[
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
],
[
- PyCalendarPeriod.parseText("20080601T160000Z/20080601T170000Z"),
+ Period.parseText("20080601T160000Z/20080601T170000Z"),
],
],
"20080601T000000Z",
@@ -211,7 +211,7 @@
(
"#1.7 With single busy time and event details",
[
- [PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"), ],
+ [Period.parseText("20080601T120000Z/20080601T130000Z"), ],
[],
[],
],
@@ -269,7 +269,7 @@
self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
yield self.populate()
- self.now = PyCalendarDateTime.getNowUTC()
+ self.now = DateTime.getNowUTC()
self.now.setHHMMSS(0, 0, 0)
self.now_12H = self.now.duplicate()
@@ -368,7 +368,7 @@
timerange = caldavxml.TimeRange(start=self.now.getText(), end=self.now_1D.getText())
result = (yield generateFreeBusyInfo(calendar, fbinfo, timerange, matchtotal))
self.assertEqual(result, 1)
- self.assertEqual(fbinfo[0], [PyCalendarPeriod.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
+ self.assertEqual(fbinfo[0], [Period.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
self.assertEqual(len(fbinfo[1]), 0)
self.assertEqual(len(fbinfo[2]), 0)
@@ -406,7 +406,7 @@
event_details=event_details
))
self.assertEqual(result, 1)
- self.assertEqual(fbinfo[0], [PyCalendarPeriod.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
+ self.assertEqual(fbinfo[0], [Period.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
self.assertEqual(len(fbinfo[1]), 0)
self.assertEqual(len(fbinfo[2]), 0)
self.assertEqual(len(event_details), 1)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,7 +14,7 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twisted.trial import unittest
from twistedcaldav.stdconfig import config
from twistedcaldav.ical import Component
@@ -31,7 +31,7 @@
self.subs = {}
- self.now = PyCalendarDateTime.getNowUTC()
+ self.now = DateTime.getNowUTC()
self.now.setHHMMSS(0, 0, 0)
self.subs["now"] = self.now
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -14,8 +14,8 @@
# limitations under the License.
##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twext.python.clsprop import classproperty
from twext.web2 import responsecode
@@ -248,9 +248,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -287,12 +287,12 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -330,15 +330,15 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -447,7 +447,7 @@
""",
(
("mailto:user3 at example.com", None),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -502,7 +502,7 @@
END:VCALENDAR
""",
(
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -648,9 +648,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -705,7 +705,7 @@
""",
(
("mailto:user3 at example.com", None),
- ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -751,7 +751,7 @@
END:VCALENDAR
""",
(
- ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
(
@@ -798,9 +798,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
),
),
)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,8 +15,8 @@
##
from __future__ import print_function
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twisted.trial import unittest
@@ -2025,7 +2025,7 @@
END:VCALENDAR
""",
("mailto:user2 at example.com",),
- (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
+ (DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
),
# Recurring component with one instance, each with one attendee - cancel instance
@@ -2066,7 +2066,7 @@
END:VCALENDAR
""",
("mailto:user2 at example.com",),
- (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
+ (DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
),
# Recurring component with one instance, each with one attendee - cancel master
@@ -2127,7 +2127,7 @@
""",
"",
("mailto:user2 at example.com",),
- (PyCalendarDateTime(2008, 12, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
+ (DateTime(2008, 12, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
),
)
@@ -2229,7 +2229,8 @@
DTSTART;TZID=America/Los_Angeles:20101007T113000
DTEND;TZID=America/Los_Angeles:20101007T120000
ATTENDEE;CN=Missing Attendee;CUTYPE=INDIVIDUAL;EMAIL=missing at example.com;P
- ARTSTAT=DECLINED;ROLE=OPT-PARTICIPANT;RSVP=TRUE:mailto:missing at example.com
+ ARTSTAT=DECLINED;ROLE=OPT-PARTICIPANT;RSVP=TRUE:mailto:missing at example.co
+ m
ORGANIZER;CN=The Organizer:mailto:organizer at example.com
REQUEST-STATUS:2.0;Success
SEQUENCE:24
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -18,7 +18,7 @@
Tests for calendarserver.tools.purge
"""
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twisted.internet.defer import inlineCallbacks
from twisted.trial import unittest
@@ -30,7 +30,7 @@
from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
-now = PyCalendarDateTime.getToday().getYear()
+now = DateTime.getToday().getYear()
ORGANIZER_ICS = """BEGIN:VCALENDAR
VERSION:2.0
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -29,7 +29,7 @@
one of them to avoid scheduling problems.
"""
- if record and record.locallyHosted():
+ if record and record.thisServer():
# Get record's calendar-home
calendar_home = yield txn.calendarHomeWithUID(record.uid)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -47,7 +47,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.python import hashlib
-from twistedcaldav import caldavxml, customxml
+from twistedcaldav import caldavxml, customxml, ical
from twistedcaldav.config import config
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.dateops import normalizeForIndex, datetimeMktime, \
@@ -89,11 +89,13 @@
InvalidUIDError, UIDExistsError, UIDExistsElsewhereError, \
InvalidResourceMove, InvalidComponentForStoreError
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.value import PyCalendarValue
+from txdav.idav import ChangeCategory
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
+from pycalendar.value import Value
+
from zope.interface.declarations import implements
from urlparse import urlparse, urlunparse
@@ -406,6 +408,26 @@
_cacher = Memcacher("SQL.calhome", pickle=True, key_normalization=False)
+ _componentCalendarName = {
+ "VEVENT": "calendar",
+ "VTODO": "tasks",
+ "VJOURNAL": "journals",
+ "VAVAILABILITY": "available",
+ "VPOLL": "polls",
+ }
+
+ _componentDefaultColumn = {
+ "VEVENT": schema.CALENDAR_HOME_METADATA.DEFAULT_EVENTS,
+ "VTODO": schema.CALENDAR_HOME_METADATA.DEFAULT_TASKS,
+ "VPOLL": schema.CALENDAR_HOME_METADATA.DEFAULT_POLLS,
+ }
+
+ _componentDefaultAttribute = {
+ "VEVENT": "_default_events",
+ "VTODO": "_default_tasks",
+ "VPOLL": "_default_polls",
+ }
+
def __init__(self, transaction, ownerUID):
self._childClass = Calendar
@@ -422,9 +444,8 @@
# Common behavior is to have created and modified
- return (
- cls._homeMetaDataSchema.DEFAULT_EVENTS,
- cls._homeMetaDataSchema.DEFAULT_TASKS,
+ default_collections = tuple([cls._componentDefaultColumn[name] for name in sorted(cls._componentDefaultColumn.keys())])
+ return default_collections + (
cls._homeMetaDataSchema.ALARM_VEVENT_TIMED,
cls._homeMetaDataSchema.ALARM_VEVENT_ALLDAY,
cls._homeMetaDataSchema.ALARM_VTODO_TIMED,
@@ -445,9 +466,8 @@
# Common behavior is to have created and modified
- return (
- "_default_events",
- "_default_tasks",
+ default_attributes = tuple([cls._componentDefaultAttribute[name] for name in sorted(cls._componentDefaultAttribute.keys())])
+ return default_attributes + (
"_alarm_vevent_timed",
"_alarm_vevent_allday",
"_alarm_vtodo_timed",
@@ -624,22 +644,18 @@
@inlineCallbacks
def createdHome(self):
- # Default calendar
- defaultCal = yield self.createCalendarWithName("calendar")
-
# Check whether components type must be separate
if config.RestrictCalendarsToOneComponentType:
- yield defaultCal.setSupportedComponents("VEVENT")
- yield self.setDefaultCalendar(defaultCal, False)
-
- # Default tasks
- defaultTasks = yield self.createCalendarWithName("tasks")
- yield defaultTasks.setSupportedComponents("VTODO")
- yield defaultTasks.setUsedForFreeBusy(False)
- yield self.setDefaultCalendar(defaultTasks, True)
+ for name in ical.allowedStoreComponents:
+ cal = yield self.createCalendarWithName(self._componentCalendarName[name])
+ yield cal.setSupportedComponents(name)
+ if name not in ("VEVENT", "VAVAILABILITY",):
+ yield cal.setUsedForFreeBusy(False)
+ yield self.setDefaultCalendar(cal, name)
else:
- yield self.setDefaultCalendar(defaultCal, False)
- yield self.setDefaultCalendar(defaultCal, True)
+ cal = yield self.createCalendarWithName("calendar")
+ for name in ical.allowedStoreComponents:
+ yield self.setDefaultCalendar(cal, name)
inbox = yield self.createCalendarWithName("inbox")
yield inbox.setUsedForFreeBusy(False)
@@ -693,55 +709,13 @@
newcal = yield self.createCalendarWithName(newname)
yield newcal.setSupportedComponents(support_component)
- yield _requireCalendarWithType("VEVENT", "calendar")
- yield _requireCalendarWithType("VTODO", "tasks")
+ for name in ical.allowedStoreComponents:
+ yield _requireCalendarWithType(name, self._componentCalendarName[name])
@inlineCallbacks
- def pickNewDefaultCalendar(self, tasks=False):
+ def setDefaultCalendar(self, calendar, componentType):
"""
- First see if default provisioned calendar exists in the calendar home and pick that. Otherwise
- pick another from the calendar home.
- """
-
- componentType = "VTODO" if tasks else "VEVENT"
- test_name = "tasks" if tasks else "calendar"
-
- defaultCalendar = (yield self.calendarWithName(test_name))
- if defaultCalendar is None or not defaultCalendar.owned():
-
- @inlineCallbacks
- def _findDefault():
- for calendarName in (yield self.listCalendars()):
- calendar = (yield self.calendarWithName(calendarName))
- if calendar.isInbox():
- continue
- if not calendar.owned():
- continue
- if not calendar.isSupportedComponent(componentType):
- continue
- break
- else:
- calendar = None
- returnValue(calendar)
-
- defaultCalendar = yield _findDefault()
- if defaultCalendar is None:
- # Create a default and try and get its name again
- yield self.ensureDefaultCalendarsExist()
- defaultCalendar = yield _findDefault()
- if defaultCalendar is None:
- # Failed to even create a default - bad news...
- raise RuntimeError("No valid calendars to use as a default %s calendar." % (componentType,))
-
- yield self.setDefaultCalendar(defaultCalendar, tasks)
-
- returnValue(defaultCalendar)
-
-
- @inlineCallbacks
- def setDefaultCalendar(self, calendar, tasks=False):
- """
Set the default calendar for a particular type of component.
@param calendar: the calendar being set as the default
@@ -749,10 +723,15 @@
@param tasks: C{True} for VTODO, C{False} for VEVENT
@type componentType: C{bool}
"""
+
+ # We only support VEVENT and VTOTO right now
+ componentType = componentType.upper()
+ if componentType not in self._componentDefaultAttribute:
+ returnValue(None)
+
chm = self._homeMetaDataSchema
- componentType = "VTODO" if tasks else "VEVENT"
- attribute_to_test = "_default_tasks" if tasks else "_default_events"
- column_to_set = chm.DEFAULT_TASKS if tasks else chm.DEFAULT_EVENTS
+ attribute_to_test = self._componentDefaultAttribute[componentType]
+ column_to_set = self._componentDefaultColumn[componentType]
# Check validity of the default
if calendar.isInbox():
@@ -794,8 +773,13 @@
@rtype: L{Calendar} or C{None}
"""
+ # We only support VEVENT and VTOTO right now
+ componentType = componentType.upper()
+ if componentType not in self._componentDefaultAttribute:
+ returnValue(None)
+
# Check any default calendar property first - this will create if none exists
- attribute_to_test = "_default_tasks" if componentType == "VTODO" else "_default_events"
+ attribute_to_test = self._componentDefaultAttribute[componentType]
defaultID = getattr(self, attribute_to_test)
if defaultID:
default = (yield self.childWithID(defaultID))
@@ -816,7 +800,8 @@
# Try to find a calendar supporting the required component type. If there are multiple, pick
# the one with the oldest created timestamp as that will likely be the initial provision.
- for calendarName in (yield self.listCalendars()):
+ existing_names = (yield self.listCalendars())
+ for calendarName in existing_names:
calendar = (yield self.calendarWithName(calendarName))
if calendar.isInbox():
continue
@@ -832,12 +817,15 @@
if not create:
returnValue(None)
else:
- new_name = "%ss" % (componentType.lower()[1:],)
+ # Try a default name mapping first, else use a UUID
+ new_name = self._componentCalendarName[componentType]
+ if new_name in existing_names:
+ new_name = str(uuid.uuid4())
default = yield self.createCalendarWithName(new_name)
- yield default.setSupportedComponents(componentType.upper())
+ yield default.setSupportedComponents(componentType)
# Update the metadata
- yield self.setDefaultCalendar(default, componentType == "VTODO")
+ yield self.setDefaultCalendar(default, componentType)
returnValue(default)
@@ -847,7 +835,10 @@
Is the supplied calendar one of the possible default calendars.
"""
# Not allowed to delete the default calendar
- return calendar._resourceID in (self._default_events, self._default_tasks)
+ for attr in self._componentDefaultAttribute.values():
+ if calendar._resourceID == getattr(self, attr):
+ return True
+ return False
ALARM_DETAILS = {
(True, True): (_homeMetaDataSchema.ALARM_VEVENT_TIMED, "_alarm_vevent_timed"),
@@ -1736,20 +1727,23 @@
NB Do this before implicit scheduling as we don't want old clients to trigger scheduling when
the X- property is missing.
+
+ We now only preserve the "X-CALENDARSERVER-ATTENDEE-COMMENT" property. We will now allow clients
+ to delete the "X-CALENDARSERVER-PRIVATE-COMMENT" and treat that as a removal of the attendee
+ comment (which will trigger scheduling with the organizer to remove the comment on the organizer's
+ side).
"""
if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
old_has_private_comments = not inserting and self.hasPrivateComment
new_has_private_comments = component.hasPropertyInAnyComponent((
- "X-CALENDARSERVER-PRIVATE-COMMENT",
"X-CALENDARSERVER-ATTENDEE-COMMENT",
))
if old_has_private_comments and not new_has_private_comments:
# Transfer old comments to new calendar
- log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
+ log.debug("Organizer private comment properties were entirely removed by the client. Restoring existing properties.")
old_calendar = (yield self.componentForUser())
component.transferProperties(old_calendar, (
- "X-CALENDARSERVER-PRIVATE-COMMENT",
"X-CALENDARSERVER-ATTENDEE-COMMENT",
))
@@ -2200,8 +2194,18 @@
else:
yield self._calendar._updateRevision(self._name)
- yield self._calendar.notifyChanged()
+ # Determine change category
+ category = ChangeCategory.default
+ if internal_state == ComponentUpdateState.INBOX:
+ category = ChangeCategory.inbox
+ elif internal_state == ComponentUpdateState.ORGANIZER_ITIP_UPDATE:
+ category = ChangeCategory.organizerITIPUpdate
+ elif (internal_state == ComponentUpdateState.ATTENDEE_ITIP_UPDATE and
+ hasattr(self._txn, "doing_attende_refresh")):
+ category = ChangeCategory.attendeeITIPUpdate
+ yield self._calendar.notifyChanged(category=category)
+
# Finally check if a split is needed
if internal_state not in (ComponentUpdateState.SPLIT_OWNER, ComponentUpdateState.SPLIT_ATTENDEE,) and schedule_state == "organizer":
yield self.checkSplit()
@@ -2248,7 +2252,7 @@
# When there is no master we have a set of overridden components -
# index them all.
# When there is one instance - index it.
- expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ expand = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
doInstanceIndexing = True
else:
@@ -2260,8 +2264,8 @@
# by default. This is a caching parameter which affects the size of the index;
# it does not affect search results beyond this period, but it may affect
# performance of such a search.
- expand = (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
+ expand = (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandAheadDays))
if expand_until and expand_until > expand:
expand = expand_until
@@ -2278,12 +2282,12 @@
# occurrences into some obscenely far-in-the-future date, so we cap the caching
# period. Searches beyond this period will always be relatively expensive for
# resources with occurrences beyond this period.
- if expand > (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
+ if expand > (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandMaxDays)):
raise IndexedSearchException
if config.FreeBusyIndexLowerLimitDays:
- truncateLowerLimit = PyCalendarDateTime.getToday()
+ truncateLowerLimit = DateTime.getToday()
truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
else:
truncateLowerLimit = None
@@ -2310,7 +2314,7 @@
if not doInstanceIndexing:
instances = None
recurrenceLowerLimit = None
- recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
co = schema.CALENDAR_OBJECT
tr = schema.TIME_RANGE
@@ -2429,7 +2433,7 @@
@param instances: the set of instances to add
@type instances: L{InstanceList}
@param truncateLowerLimit: the lower limit for instances
- @type truncateLowerLimit: L{PyCalendarDateTime}
+ @type truncateLowerLimit: L{DateTime}
@param isInboxItem: indicates if an inbox item
@type isInboxItem: C{bool}
@param txn: transaction to use
@@ -2458,8 +2462,8 @@
# For truncated items we insert a tomb stone lower bound so that a time-range
# query with just an end bound will match
if lowerLimitApplied or instances.lowerLimit and len(instances.instances) == 0:
- start = PyCalendarDateTime(1901, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(1901, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(1901, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(1901, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", isInboxItem, txn)
# Special - for unbounded recurrence we insert a value for "infinity"
@@ -2467,8 +2471,8 @@
# We also need to add the "infinity" value if the event was bounded but
# starts after the future expansion cut-off limit.
if component.isRecurringUnbounded() or instances.limit and len(instances.instances) == 0:
- start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ start = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(2100, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", isInboxItem, txn)
@@ -2655,7 +2659,7 @@
Get the RECURRANCE_MIN, RECURRANCE_MAX value from the database. Occasionally we might need to do an
update to time-range data via a separate transaction, so we allow that to be passed in.
- @return: L{PyCalendarDateTime} result
+ @return: L{DateTime} result
"""
# Setup appropriate txn
txn = txn if txn is not None else self._txn
@@ -4277,7 +4281,7 @@
"""
Return an iCalendar ATTACH property for this attachment.
"""
- attach = Property("ATTACH", "", valuetype=PyCalendarValue.VALUETYPE_URI)
+ attach = Property("ATTACH", "", valuetype=Value.VALUETYPE_URI)
location = (yield self.updateProperty(attach))
returnValue((attach, location,))
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -53,7 +53,9 @@
from txdav.common.icommondatastore import ConcurrentModification
from twistedcaldav.ical import Component
from twistedcaldav.config import config
+from calendarserver.push.util import PushPriority
+
storePath = FilePath(__file__).parent().child("calendar_store")
homeRoot = storePath.child("ho").child("me").child("home1")
@@ -456,8 +458,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
])
)
yield self.commit()
@@ -474,8 +476,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
])
)
yield self.commit()
@@ -698,7 +700,7 @@
calendarProperties = (yield home.calendarWithName(name)).properties()
self.assertEqual(len(calendarProperties), 0)
# notify is called prior to commit
- self.assertTrue("/CalDAV/example.com/home1/" in self.notifierFactory.history)
+ self.assertTrue(("/CalDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
yield self.commit()
# Make sure it's available in a new transaction; i.e. test the commit.
@@ -741,10 +743,10 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home1/calendar_2/",
- "/CalDAV/example.com/home1/calendar_empty/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_2/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_empty/", PushPriority.high),
])
)
@@ -918,8 +920,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
@@ -1337,7 +1339,7 @@
@inlineCallbacks
def test_iCalendarText(self):
"""
- L{ICalendarObject.iCalendarText} returns a C{str} describing the same
+ L{ICalendarObject._text} returns a C{str} describing the same
data provided by L{ICalendarObject.component}.
"""
text = yield (yield self.calendarObjectUnderTest())._text()
@@ -1474,8 +1476,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
@@ -1593,8 +1595,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -16,8 +16,8 @@
from calendarserver.tap.util import directoryFromConfig
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.value import PyCalendarValue
+from pycalendar.datetime import DateTime
+from pycalendar.value import Value
from twext.enterprise.dal.syntax import Delete
from twext.python.clsprop import classproperty
@@ -1249,7 +1249,7 @@
-now = PyCalendarDateTime.getToday().getYear()
+now = DateTime.getToday().getYear()
PLAIN_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -1454,7 +1454,7 @@
cal.mainComponent().addProperty(Property(
"ATTACH",
"http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (home.name(), dropboxid, name,),
- valuetype=PyCalendarValue.VALUETYPE_URI
+ valuetype=Value.VALUETYPE_URI
))
yield event.setComponent(cal)
yield txn.commit()
@@ -1477,7 +1477,7 @@
cal.mainComponent().addProperty(Property(
"ATTACH",
"http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (owner_home, dropboxid, name,),
- valuetype=PyCalendarValue.VALUETYPE_URI
+ valuetype=Value.VALUETYPE_URI
))
yield event.setComponent(cal)
yield txn.commit()
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -42,7 +42,7 @@
from txdav.caldav.datastore.test.common import (
CommonTests, test_event_text, event1modified_text)
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
storePath = FilePath(__file__).parent().child("calendar_store")
@@ -68,7 +68,7 @@
storePath.copyTo(calendarPath)
# Set year values to current year
- nowYear = PyCalendarDateTime.getToday().getYear()
+ nowYear = DateTime.getToday().getYear()
for home in calendarPath.child("ho").child("me").children():
if not home.basename().startswith("."):
for calendar in home.children():
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -478,9 +478,9 @@
@inlineCallbacks
- def test_validation_preservePrivateComments(self):
+ def test_validation_noPreservePrivateComments(self):
"""
- Test that resource private comments are restored.
+ Test that attendee private comments are no longer restored.
"""
data1 = """BEGIN:VCALENDAR
@@ -524,12 +524,65 @@
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar1 = (yield calendar_resource.component())
calendar1 = str(calendar1).replace("\r\n ", "")
- self.assertTrue("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
+ self.assertFalse("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
self.assertTrue("SUMMARY:Changed" in calendar1)
yield self.commit()
@inlineCallbacks
+ def test_validation_preserveOrganizerPrivateComments(self):
+ """
+ Test that organizer private comments are restored.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:user01";
+ X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=\"urn:uuid:user01\";X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
def test_validation_replaceMissingToDoProperties_OrganizerAttendee(self):
"""
Test that missing scheduling properties in VTODOs are recovered.
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,7 +30,7 @@
from twistedcaldav.test.util import InMemoryMemcacheProtocol
import twistedcaldav.test.util
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
import os
@@ -311,7 +311,7 @@
else:
self.assertFalse(self.db.resourceExists(name), msg=description)
- self.db.testAndUpdateIndex(PyCalendarDateTime(2020, 1, 1))
+ self.db.testAndUpdateIndex(DateTime(2020, 1, 1))
for description, name, calendar_txt, reCreate, ok in data:
if ok:
self.assertTrue(self.db.resourceExists(name), msg=description)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -27,8 +27,8 @@
L{txdav.caldav.datastore.test.common}.
"""
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
from twext.enterprise.dal.syntax import Select, Parameter, Insert, Delete, \
Update
@@ -42,7 +42,7 @@
from twisted.internet.task import deferLater
from twisted.trial import unittest
-from twistedcaldav import caldavxml
+from twistedcaldav import caldavxml, ical
from twistedcaldav.caldavxml import CalendarDescription
from twistedcaldav.config import config
from twistedcaldav.dateops import datetimeMktime
@@ -78,7 +78,7 @@
self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
yield self.populate()
- self.nowYear = {"now": PyCalendarDateTime.getToday().getYear()}
+ self.nowYear = {"now": DateTime.getToday().getYear()}
@inlineCallbacks
@@ -474,14 +474,14 @@
)
supported_components = set()
- self.assertEqual(len(toCalendars), 4)
+ self.assertEqual(len(toCalendars), 2 + len(ical.allowedStoreComponents))
for calendar in toCalendars:
if calendar.name() == "inbox":
continue
result = yield calendar.getSupportedComponents()
supported_components.add(result)
- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
@inlineCallbacks
@@ -509,7 +509,7 @@
result = yield calendar.getSupportedComponents()
supported_components.add(result)
- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
def test_calendarHomeVersion(self):
@@ -1124,7 +1124,7 @@
result = yield calendar.getSupportedComponents()
supported_components.add(result)
- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
@inlineCallbacks
@@ -1190,7 +1190,7 @@
self.assertEqual(home._default_events, None)
self.assertEqual(home._default_tasks, None)
calendar1 = yield home.calendarWithName("calendar_1")
- yield home.setDefaultCalendar(calendar1, False)
+ yield home.setDefaultCalendar(calendar1, "VEVENT")
self.assertEqual(home._default_events, calendar1._resourceID)
self.assertEqual(home._default_tasks, None)
yield self.commit()
@@ -1198,7 +1198,7 @@
home = yield self.homeUnderTest(name="home_defaults")
calendar1 = yield home.calendarWithName("calendar_1")
calendar2 = yield home.calendarWithName("calendar_1-vtodo")
- yield self.failUnlessFailure(home.setDefaultCalendar(calendar2, False), InvalidDefaultCalendar)
+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar2, "VEVENT"), InvalidDefaultCalendar)
self.assertEqual(home._default_events, calendar1._resourceID)
self.assertEqual(home._default_tasks, None)
yield self.commit()
@@ -1206,20 +1206,20 @@
home = yield self.homeUnderTest(name="home_defaults")
calendar1 = yield home.calendarWithName("calendar_1")
calendar2 = yield home.calendarWithName("calendar_1-vtodo")
- yield home.setDefaultCalendar(calendar2, True)
+ yield home.setDefaultCalendar(calendar2, "VTODO")
self.assertEqual(home._default_events, calendar1._resourceID)
self.assertEqual(home._default_tasks, calendar2._resourceID)
yield self.commit()
home = yield self.homeUnderTest(name="home_defaults")
calendar1 = yield home.calendarWithName("inbox")
- yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, "VEVENT"), InvalidDefaultCalendar)
yield self.commit()
home = yield self.homeUnderTest(name="home_defaults")
home_other = yield self.homeUnderTest(name="home_splits")
calendar1 = yield home_other.calendarWithName("calendar_1")
- yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, "VEVENT"), InvalidDefaultCalendar)
yield self.commit()
@@ -1377,38 +1377,38 @@
self.assertEqual(rmax.getYear(), nowYear + 1)
# Fully within range
- testMin = PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMin = DateTime(nowYear, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, [])
# Upper bound exceeded
- testMin = PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMin = DateTime(nowYear, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, ["indexing.ics"])
# Lower bound exceeded
- testMin = PyCalendarDateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMin = DateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, ["indexing.ics"])
# Lower and upper bounds exceeded
- testMin = PyCalendarDateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMin = DateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, ["indexing.ics"])
# Lower none within range
testMin = None
- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, [])
# Lower none and upper bounds exceeded
testMin = None
- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
result = yield index.notExpandedWithin(testMin, testMax)
self.assertEqual(result, ["indexing.ics"])
@@ -2154,7 +2154,7 @@
self.subs = {}
- self.now = PyCalendarDateTime.getNowUTC()
+ self.now = DateTime.getNowUTC()
self.now.setHHMMSS(0, 0, 0)
self.subs["now"] = self.now
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -349,7 +349,8 @@
"inbox": {},
# XXX: implementation is configuration-sensitive regarding the
# 'tasks' calendar and it shouldn't be.
- "tasks": {}
+ "tasks": {},
+ "polls": {},
}
}, self.storeUnderTest())
txn = self.transactionUnderTest()
@@ -412,7 +413,7 @@
c1 = {"1.ics": self.sampleEvent("uid1")}
if c2 is None:
c2 = {"2.ics": self.sampleEvent("uid2")}
- defaults = {"calendar": {}, "inbox": {}, "tasks": {}}
+ defaults = {"calendar": {}, "inbox": {}, "tasks": {}, "polls": {}}
def conflicted(caldata):
d = defaults.copy()
d.update(conflicted=caldata)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -61,7 +61,6 @@
fullName,
calendarUserAddresses,
cutype="INDIVIDUAL",
- locallyHosted=True,
thisServer=True,
):
@@ -72,7 +71,6 @@
self.displayName = self.fullName if self.fullName else self.shortNames[0]
self.calendarUserAddresses = calendarUserAddresses
self.cutype = cutype
- self._locallyHosted = locallyHosted
self._thisServer = thisServer
@@ -92,10 +90,6 @@
return cua
- def locallyHosted(self):
- return self._locallyHosted
-
-
def thisServer(self):
return self._thisServer
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -37,6 +37,7 @@
from twext.python.vcomponent import InvalidICalendarDataError
from twext.python.vcomponent import VComponent
+from twistedcaldav import ical
from twistedcaldav.datafilters.hiddeninstance import HiddenInstanceFilter
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
from twistedcaldav.ical import PERUSER_UID
@@ -380,9 +381,11 @@
"""
from twistedcaldav.config import config
if not merge:
- yield outHome.removeCalendarWithName("calendar")
if config.RestrictCalendarsToOneComponentType:
- yield outHome.removeCalendarWithName("tasks")
+ for name in ical.allowedStoreComponents:
+ yield outHome.removeCalendarWithName(outHome._componentCalendarName[name])
+ else:
+ yield outHome.removeCalendarWithName("calendar")
yield outHome.removeCalendarWithName("inbox")
outHome.properties().update(inHome.properties())
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -60,18 +60,9 @@
@rtype: C{str}
"""
- def locallyHosted(): #@NoSelf
- """
- Indicates whether the record is host on this specific server "pod".
-
- @return: C{True} if locally hosted.
- @rtype: C{bool}
- """
-
def thisServer(): #@NoSelf
"""
- Indicates whether the record is hosted on this server or another "pod"
- that hosts the same directory service.
+ Indicates whether the record is hosted on this server "pod".
@return: C{True} if hosted by this service.
@rtype: C{bool}
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -363,9 +363,9 @@
instances that occur within the time range that begins at
C{start} and ends at C{end}.
- @param start: a L{PyCalendarDateTime}.
- @param end: a L{PyCalendarDateTime}.
- @param timeZone: a L{PyCalendarTimezone}.
+ @param start: a L{DateTime}.
+ @param end: a L{DateTime}.
+ @param timeZone: a L{Timezone}.
@return: an iterable of L{ICalendarObject}s.
"""
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -39,7 +39,9 @@
from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError
from txdav.idav import IPropertyStore, IDataStore
from txdav.xml.element import WebDAVUnknownElement
+from calendarserver.push.util import PushPriority
+
storePath = FilePath(__file__).parent().child("addressbook_store")
home1Root = storePath.child("ho").child("me").child("home1")
@@ -372,7 +374,7 @@
yield home.removeAddressBookWithName(name)
self.assertNotIdentical((yield home.addressbookWithName(name)), None)
# notify is called prior to commit
- self.assertTrue("/CardDAV/example.com/home1/" in self.notifierFactory.history)
+ self.assertTrue(("/CardDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
yield self.commit()
# Make sure it's available in a new transaction; i.e. test the commit.
@@ -399,8 +401,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -532,8 +534,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -693,8 +695,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -809,8 +811,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -27,7 +27,7 @@
from cStringIO import StringIO
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from twext.enterprise.dal.syntax import (
Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction,
@@ -74,6 +74,7 @@
from txdav.common.inotifications import INotificationCollection, \
INotificationObject
from txdav.xml.parser import WebDAVDocument
+from txdav.idav import ChangeCategory
from uuid import uuid4, UUID
@@ -1107,7 +1108,7 @@
def eventsOlderThan(self, cutoff, batchSize=None):
"""
Return up to the oldest batchSize events which exist completely earlier
- than "cutoff" (PyCalendarDateTime)
+ than "cutoff" (DateTime)
Returns a deferred to a list of (uid, calendarName, eventName, maxDate)
tuples.
@@ -1115,7 +1116,7 @@
# Make sure cut off is after any lower limit truncation in the DB
if config.FreeBusyIndexLowerLimitDays:
- truncateLowerLimit = PyCalendarDateTime.getToday()
+ truncateLowerLimit = DateTime.getToday()
truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
if cutoff < truncateLowerLimit:
raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
@@ -1133,7 +1134,7 @@
# Make sure cut off is after any lower limit truncation in the DB
if config.FreeBusyIndexLowerLimitDays:
- truncateLowerLimit = PyCalendarDateTime.getToday()
+ truncateLowerLimit = DateTime.getToday()
truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
if cutoff < truncateLowerLimit:
raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
@@ -2203,7 +2204,7 @@
@inlineCallbacks
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -2227,7 +2228,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
@classproperty
@@ -4335,11 +4336,11 @@
return self.ownerHome().notifierID()
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications when a child resource is changed.
"""
- return self._notifyChanged(property_change=False)
+ return self._notifyChanged(property_change=False, category=category)
def notifyPropertyChanged(self):
@@ -4350,7 +4351,8 @@
@inlineCallbacks
- def _notifyChanged(self, property_change=False):
+ def _notifyChanged(self, property_change=False,
+ category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -4386,7 +4388,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
@classproperty
@@ -5200,7 +5202,7 @@
@inlineCallbacks
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -5219,7 +5221,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
returnValue(None)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -43,8 +43,8 @@
from twext.python.clsprop import classproperty
from twext.python.log import Logger
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
log = Logger()
@@ -566,7 +566,7 @@
)
if qualifiers is not None:
- today = PyCalendarDateTime.getToday()
+ today = DateTime.getToday()
# Determine how far we need to extend the current expansion of
# events. If we have an open-ended time-range we will expand
@@ -578,11 +578,11 @@
maxDate = maxDate.duplicate()
maxDate.offsetDay(1)
maxDate.setDateOnly(True)
- upperLimit = today + PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)
+ upperLimit = today + Duration(days=config.FreeBusyIndexExpandMaxDays)
if maxDate > upperLimit:
raise TimeRangeUpperLimit(upperLimit)
if isStartDate:
- maxDate += PyCalendarDuration(days=365)
+ maxDate += Duration(days=365)
# Determine if the start date is too early for the restricted range we
# are applying. If it is today or later we don't need to worry about truncation
@@ -591,7 +591,7 @@
if minDate >= today:
minDate = None
if minDate is not None and config.FreeBusyIndexLowerLimitDays:
- truncateLowerLimit = today - PyCalendarDuration(days=config.FreeBusyIndexLowerLimitDays)
+ truncateLowerLimit = today - Duration(days=config.FreeBusyIndexLowerLimitDays)
if minDate < truncateLowerLimit:
raise TimeRangeLowerLimit(truncateLowerLimit)
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -30,6 +30,7 @@
"QUOTA_USED_BYTES" integer default 0 not null,
"DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
"DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
"ALARM_VEVENT_TIMED" nclob default null,
"ALARM_VEVENT_ALLDAY" nclob default null,
"ALARM_VTODO_TIMED" nclob default null,
@@ -349,7 +350,8 @@
create table PUSH_NOTIFICATION_WORK (
"WORK_ID" integer primary key not null,
"NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PUSH_ID" nvarchar2(255)
+ "PUSH_ID" nvarchar2(255),
+ "PRIORITY" integer not null
);
create table GROUP_CACHER_POLLING_WORK (
@@ -368,7 +370,7 @@
"VALUE" nvarchar2(255)
);
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '29');
insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
@@ -379,6 +381,10 @@
DEFAULT_TASKS
);
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
NOTIFICATION_HOME_RESOURCE_ID
);
@@ -450,9 +456,11 @@
CALENDAR_RESOURCE_ID
);
-create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
CALENDAR_RESOURCE_ID,
- RESOURCE_NAME
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
);
create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
@@ -465,9 +473,11 @@
OWNER_HOME_RESOURCE_ID
);
-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
OWNER_HOME_RESOURCE_ID,
- RESOURCE_NAME
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
);
create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -74,6 +74,7 @@
QUOTA_USED_BYTES integer default 0 not null,
DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
ALARM_VEVENT_TIMED text default null,
ALARM_VEVENT_ALLDAY text default null,
ALARM_VTODO_TIMED text default null,
@@ -87,6 +88,8 @@
CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+ CALENDAR_HOME_METADATA(DEFAULT_POLLS);
-----------------------
-- Calendar Metadata --
@@ -504,7 +507,7 @@
MESSAGE text, -- FIXME: xml?
primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
);
create index SHARED_GROUP_BIND_RESOURCE_ID on
@@ -534,8 +537,8 @@
create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
@@ -558,8 +561,8 @@
create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME);
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
@@ -669,7 +672,8 @@
create table PUSH_NOTIFICATION_WORK (
WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PUSH_ID varchar(255) not null
+ PUSH_ID varchar(255) not null,
+ PRIORITY integer not null -- 1:low 5:medium 10:high
);
-----------------
@@ -704,6 +708,6 @@
VALUE varchar(255)
);
-insert into CALENDARSERVER values ('VERSION', '26');
+insert into CALENDARSERVER values ('VERSION', '29');
insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,491 +0,0 @@
-create sequence RESOURCE_ID_SEQ;
-create sequence INSTANCE_ID_SEQ;
-create sequence ATTACHMENT_ID_SEQ;
-create sequence REVISION_SEQ;
-create sequence WORKITEM_SEQ;
-create table NODE_INFO (
- "HOSTNAME" nvarchar2(255),
- "PID" integer not null,
- "PORT" integer not null,
- "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
- primary key("HOSTNAME", "PORT")
-);
-
-create table NAMED_LOCK (
- "LOCK_NAME" nvarchar2(255) primary key
-);
-
-create table CALENDAR_HOME (
- "RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique,
- "DATAVERSION" integer default 0 not null
-);
-
-create table CALENDAR (
- "RESOURCE_ID" integer primary key
-);
-
-create table CALENDAR_HOME_METADATA (
- "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
- "QUOTA_USED_BYTES" integer default 0 not null,
- "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
- "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
- "ALARM_VEVENT_TIMED" nclob default null,
- "ALARM_VEVENT_ALLDAY" nclob default null,
- "ALARM_VTODO_TIMED" nclob default null,
- "ALARM_VTODO_ALLDAY" nclob default null,
- "AVAILABILITY" nclob default null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table CALENDAR_METADATA (
- "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
- "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table NOTIFICATION_HOME (
- "RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique
-);
-
-create table NOTIFICATION (
- "RESOURCE_ID" integer primary key,
- "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
- "NOTIFICATION_UID" nvarchar2(255),
- "XML_TYPE" nvarchar2(255),
- "XML_DATA" nclob,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
-);
-
-create table CALENDAR_BIND (
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "CALENDAR_RESOURCE_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- "TRANSP" integer default 0 not null,
- "ALARM_VEVENT_TIMED" nclob default null,
- "ALARM_VEVENT_ALLDAY" nclob default null,
- "ALARM_VTODO_TIMED" nclob default null,
- "ALARM_VTODO_ALLDAY" nclob default null,
- "TIMEZONE" nclob default null,
- primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
- unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
-);
-
-create table CALENDAR_BIND_MODE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
-create table CALENDAR_BIND_STATUS (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
-create table CALENDAR_TRANSP (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
-insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
-create table CALENDAR_OBJECT (
- "RESOURCE_ID" integer primary key,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "ICALENDAR_TEXT" nclob,
- "ICALENDAR_UID" nvarchar2(255),
- "ICALENDAR_TYPE" nvarchar2(255),
- "ATTACHMENTS_MODE" integer default 0 not null,
- "DROPBOX_ID" nvarchar2(255),
- "ORGANIZER" nvarchar2(255),
- "RECURRANCE_MIN" date,
- "RECURRANCE_MAX" date,
- "ACCESS" integer default 0 not null,
- "SCHEDULE_OBJECT" integer default 0,
- "SCHEDULE_TAG" nvarchar2(36) default null,
- "SCHEDULE_ETAGS" nclob default null,
- "PRIVATE_COMMENTS" integer default 0 not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
-);
-
-create table CALENDAR_OBJECT_ATTACHMENTS_MO (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
-create table CALENDAR_ACCESS_TYPE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(32) unique
-);
-
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
-create table TIME_RANGE (
- "INSTANCE_ID" integer primary key,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
- "FLOATING" integer not null,
- "START_DATE" timestamp not null,
- "END_DATE" timestamp not null,
- "FBTYPE" integer not null,
- "TRANSPARENT" integer not null
-);
-
-create table FREE_BUSY_TYPE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
-create table TRANSPARENCY (
- "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
- "USER_ID" nvarchar2(255),
- "TRANSPARENT" integer not null
-);
-
-create table ATTACHMENT (
- "ATTACHMENT_ID" integer primary key,
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "DROPBOX_ID" nvarchar2(255),
- "CONTENT_TYPE" nvarchar2(255),
- "SIZE" integer not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PATH" nvarchar2(1024)
-);
-
-create table ATTACHMENT_CALENDAR_OBJECT (
- "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
- "MANAGED_ID" nvarchar2(255),
- "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
- primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
- unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
-);
-
-create table RESOURCE_PROPERTY (
- "RESOURCE_ID" integer not null,
- "NAME" nvarchar2(255),
- "VALUE" nclob,
- "VIEWER_UID" nvarchar2(255),
- primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
-);
-
-create table ADDRESSBOOK_HOME (
- "RESOURCE_ID" integer primary key,
- "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
- "OWNER_UID" nvarchar2(255) unique,
- "DATAVERSION" integer default 0 not null
-);
-
-create table ADDRESSBOOK_HOME_METADATA (
- "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
- "QUOTA_USED_BYTES" integer default 0 not null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table SHARED_ADDRESSBOOK_BIND (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
-);
-
-create table ADDRESSBOOK_OBJECT (
- "RESOURCE_ID" integer primary key,
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "VCARD_TEXT" nclob,
- "VCARD_UID" nvarchar2(255),
- "KIND" integer not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
-);
-
-create table ADDRESSBOOK_OBJECT_KIND (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
-create table ABO_MEMBERS (
- "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
- primary key("GROUP_ID", "MEMBER_ID")
-);
-
-create table ABO_FOREIGN_MEMBERS (
- "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "MEMBER_ADDRESS" nvarchar2(255),
- primary key("GROUP_ID", "MEMBER_ADDRESS")
-);
-
-create table SHARED_GROUP_BIND (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
-);
-
-create table CALENDAR_OBJECT_REVISIONS (
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "CALENDAR_RESOURCE_ID" integer references CALENDAR,
- "CALENDAR_NAME" nvarchar2(255) default null,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null
-);
-
-create table ADDRESSBOOK_OBJECT_REVISIONS (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
- "ADDRESSBOOK_NAME" nvarchar2(255) default null,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null
-);
-
-create table NOTIFICATION_OBJECT_REVISIONS (
- "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null,
- unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
-);
-
-create table APN_SUBSCRIPTIONS (
- "TOKEN" nvarchar2(255),
- "RESOURCE_KEY" nvarchar2(255),
- "MODIFIED" integer not null,
- "SUBSCRIBER_GUID" nvarchar2(255),
- "USER_AGENT" nvarchar2(255) default null,
- "IP_ADDR" nvarchar2(255) default null,
- primary key("TOKEN", "RESOURCE_KEY")
-);
-
-create table IMIP_TOKENS (
- "TOKEN" nvarchar2(255),
- "ORGANIZER" nvarchar2(255),
- "ATTENDEE" nvarchar2(255),
- "ICALUID" nvarchar2(255),
- "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- primary key("ORGANIZER", "ATTENDEE", "ICALUID")
-);
-
-create table IMIP_INVITATION_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "FROM_ADDR" nvarchar2(255),
- "TO_ADDR" nvarchar2(255),
- "ICALENDAR_TEXT" nclob
-);
-
-create table IMIP_POLLING_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table IMIP_REPLY_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "ORGANIZER" nvarchar2(255),
- "ATTENDEE" nvarchar2(255),
- "ICALENDAR_TEXT" nclob
-);
-
-create table PUSH_NOTIFICATION_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PUSH_ID" nvarchar2(255)
-);
-
-create table GROUP_CACHER_POLLING_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table CALENDAR_OBJECT_SPLITTER_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
-);
-
-create table CALENDARSERVER (
- "NAME" nvarchar2(255) primary key,
- "VALUE" nvarchar2(255)
-);
-
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
-insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
-insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
-create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
- DEFAULT_EVENTS
-);
-
-create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
- DEFAULT_TASKS
-);
-
-create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
- NOTIFICATION_HOME_RESOURCE_ID
-);
-
-create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
- CALENDAR_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
- CALENDAR_RESOURCE_ID,
- ICALENDAR_UID
-);
-
-create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
- CALENDAR_RESOURCE_ID,
- RECURRANCE_MAX
-);
-
-create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
- ICALENDAR_UID
-);
-
-create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
- DROPBOX_ID
-);
-
-create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
- CALENDAR_RESOURCE_ID
-);
-
-create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
- CALENDAR_OBJECT_RESOURCE_ID
-);
-
-create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
- TIME_RANGE_INSTANCE_ID
-);
-
-create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
- CALENDAR_HOME_RESOURCE_ID
-);
-
-create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
- CALENDAR_OBJECT_RESOURCE_ID
-);
-
-create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
- OWNER_HOME_RESOURCE_ID
-);
-
-create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
- ADDRESSBOOK_ID
-);
-
-create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
- MEMBER_ID
-);
-
-create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
- ADDRESSBOOK_ID
-);
-
-create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
- GROUP_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_HOME_RESOURCE_ID,
- CALENDAR_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_RESOURCE_ID,
- RESOURCE_NAME
-);
-
-create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_RESOURCE_ID,
- REVISION
-);
-
-create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
- ADDRESSBOOK_HOME_RESOURCE_ID,
- OWNER_HOME_RESOURCE_ID
-);
-
-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
- OWNER_HOME_RESOURCE_ID,
- RESOURCE_NAME
-);
-
-create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
- OWNER_HOME_RESOURCE_ID,
- REVISION
-);
-
-create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
- NOTIFICATION_HOME_RESOURCE_ID,
- REVISION
-);
-
-create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
- RESOURCE_KEY
-);
-
-create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
- TOKEN
-);
-
-create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
- RESOURCE_ID
-);
-
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,491 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,495 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,500 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '27');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,501 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255),
+ "PRIORITY" integer not null
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '28');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,700 +0,0 @@
--- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
-
-----
--- Copyright (c) 2010-2013 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.
-----
-
-
------------------
--- Resource ID --
------------------
-
-create sequence RESOURCE_ID_SEQ;
-
-
--------------------------
--- Cluster Bookkeeping --
--------------------------
-
--- Information about a process connected to this database.
-
--- Note that this must match the node info schema in twext.enterprise.queue.
-create table NODE_INFO (
- HOSTNAME varchar(255) not null,
- PID integer not null,
- PORT integer not null,
- TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
-
- primary key (HOSTNAME, PORT)
-);
-
--- Unique named locks. This table should always be empty, but rows are
--- temporarily created in order to prevent undesirable concurrency.
-create table NAMED_LOCK (
- LOCK_NAME varchar(255) primary key
-);
-
-
--------------------
--- Calendar Home --
--------------------
-
-create table CALENDAR_HOME (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
- DATAVERSION integer default 0 not null
-);
-
---------------
--- Calendar --
---------------
-
-create table CALENDAR (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
-);
-
-----------------------------
--- Calendar Home Metadata --
-----------------------------
-
-create table CALENDAR_HOME_METADATA (
- RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
- QUOTA_USED_BYTES integer default 0 not null,
- DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
- DEFAULT_TASKS integer default null references CALENDAR on delete set null,
- ALARM_VEVENT_TIMED text default null,
- ALARM_VEVENT_ALLDAY text default null,
- ALARM_VTODO_TIMED text default null,
- ALARM_VTODO_ALLDAY text default null,
- AVAILABILITY text default null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
- CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
-create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
- CALENDAR_HOME_METADATA(DEFAULT_TASKS);
-
------------------------
--- Calendar Metadata --
------------------------
-
-create table CALENDAR_METADATA (
- RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
- SUPPORTED_COMPONENTS varchar(255) default null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
----------------------------
--- Sharing Notifications --
----------------------------
-
-create table NOTIFICATION_HOME (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique -- implicit index
-);
-
-create table NOTIFICATION (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
- NOTIFICATION_UID varchar(255) not null,
- XML_TYPE varchar(255) not null,
- XML_DATA text not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
-);
-
-create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
- NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
-
-
--------------------
--- Calendar Bind --
--------------------
-
--- Joins CALENDAR_HOME and CALENDAR
-
-create table CALENDAR_BIND (
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- CALENDAR_RESOURCE_NAME varchar(255) not null,
- BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
- BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
- BIND_REVISION integer default 0 not null,
- MESSAGE text,
- TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
- ALARM_VEVENT_TIMED text default null,
- ALARM_VEVENT_ALLDAY text default null,
- ALARM_VTODO_TIMED text default null,
- ALARM_VTODO_ALLDAY text default null,
- TIMEZONE text default null,
-
- primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
- unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
-);
-
-create index CALENDAR_BIND_RESOURCE_ID on
- CALENDAR_BIND(CALENDAR_RESOURCE_ID);
-
--- Enumeration of calendar bind modes
-
-create table CALENDAR_BIND_MODE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_BIND_MODE values (0, 'own' );
-insert into CALENDAR_BIND_MODE values (1, 'read' );
-insert into CALENDAR_BIND_MODE values (2, 'write');
-insert into CALENDAR_BIND_MODE values (3, 'direct');
-
--- Enumeration of statuses
-
-create table CALENDAR_BIND_STATUS (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_BIND_STATUS values (0, 'invited' );
-insert into CALENDAR_BIND_STATUS values (1, 'accepted');
-insert into CALENDAR_BIND_STATUS values (2, 'declined');
-insert into CALENDAR_BIND_STATUS values (3, 'invalid');
-
-
--- Enumeration of transparency
-
-create table CALENDAR_TRANSP (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_TRANSP values (0, 'opaque' );
-insert into CALENDAR_TRANSP values (1, 'transparent');
-
-
----------------------
--- Calendar Object --
----------------------
-
-create table CALENDAR_OBJECT (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- RESOURCE_NAME varchar(255) not null,
- ICALENDAR_TEXT text not null,
- ICALENDAR_UID varchar(255) not null,
- ICALENDAR_TYPE varchar(255) not null,
- ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
- DROPBOX_ID varchar(255),
- ORGANIZER varchar(255),
- RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
- RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
- ACCESS integer default 0 not null,
- SCHEDULE_OBJECT boolean default false,
- SCHEDULE_TAG varchar(36) default null,
- SCHEDULE_ETAGS text default null,
- PRIVATE_COMMENTS boolean default false not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
-
- -- since the 'inbox' is a 'calendar resource' for the purpose of storing
- -- calendar objects, this constraint has to be selectively enforced by the
- -- application layer.
-
- -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
-);
-
-create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
- CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
-
-create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
- CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
-
-create index CALENDAR_OBJECT_ICALENDAR_UID on
- CALENDAR_OBJECT(ICALENDAR_UID);
-
-create index CALENDAR_OBJECT_DROPBOX_ID on
- CALENDAR_OBJECT(DROPBOX_ID);
-
--- Enumeration of attachment modes
-
-create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
-
-
--- Enumeration of calendar access types
-
-create table CALENDAR_ACCESS_TYPE (
- ID integer primary key,
- DESCRIPTION varchar(32) not null unique
-);
-
-insert into CALENDAR_ACCESS_TYPE values (0, '' );
-insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
-insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
-insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
-insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
-
-
------------------
--- Instance ID --
------------------
-
-create sequence INSTANCE_ID_SEQ;
-
-
-----------------
--- Time Range --
-----------------
-
-create table TIME_RANGE (
- INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
- FLOATING boolean not null,
- START_DATE timestamp not null,
- END_DATE timestamp not null,
- FBTYPE integer not null,
- TRANSPARENT boolean not null
-);
-
-create index TIME_RANGE_CALENDAR_RESOURCE_ID on
- TIME_RANGE(CALENDAR_RESOURCE_ID);
-create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
- TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
-
-
--- Enumeration of free/busy types
-
-create table FREE_BUSY_TYPE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into FREE_BUSY_TYPE values (0, 'unknown' );
-insert into FREE_BUSY_TYPE values (1, 'free' );
-insert into FREE_BUSY_TYPE values (2, 'busy' );
-insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
-insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
-
-
-------------------
--- Transparency --
-------------------
-
-create table TRANSPARENCY (
- TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
- USER_ID varchar(255) not null,
- TRANSPARENT boolean not null
-);
-
-create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
- TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
-
-
-----------------
--- Attachment --
-----------------
-
-create sequence ATTACHMENT_ID_SEQ;
-
-create table ATTACHMENT (
- ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- DROPBOX_ID varchar(255),
- CONTENT_TYPE varchar(255) not null,
- SIZE integer not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PATH varchar(1024) not null
-);
-
-create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
- ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
-
--- Many-to-many relationship between attachments and calendar objects
-create table ATTACHMENT_CALENDAR_OBJECT (
- ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
- MANAGED_ID varchar(255) not null,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
-
- primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
- unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
-);
-
-create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
- ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
-
------------------------
--- Resource Property --
------------------------
-
-create table RESOURCE_PROPERTY (
- RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
- NAME varchar(255) not null,
- VALUE text not null, -- FIXME: xml?
- VIEWER_UID varchar(255),
-
- primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
-);
-
-
-----------------------
--- AddressBook Home --
-----------------------
-
-create table ADDRESSBOOK_HOME (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
- DATAVERSION integer default 0 not null
-);
-
-
--------------------------------
--- AddressBook Home Metadata --
--------------------------------
-
-create table ADDRESSBOOK_HOME_METADATA (
- RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
- QUOTA_USED_BYTES integer default 0 not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
------------------------------
--- Shared AddressBook Bind --
------------------------------
-
--- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
-
-create table SHARED_ADDRESSBOOK_BIND (
- ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
- ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
- BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
- BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
- BIND_REVISION integer default 0 not null,
- MESSAGE text, -- FIXME: xml?
-
- primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
-);
-
-create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
- SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
-
-
-------------------------
--- AddressBook Object --
-------------------------
-
-create table ADDRESSBOOK_OBJECT (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
- RESOURCE_NAME varchar(255) not null,
- VCARD_TEXT text not null,
- VCARD_UID varchar(255) not null,
- KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
-);
-
-
------------------------------
--- AddressBook Object kind --
------------------------------
-
-create table ADDRESSBOOK_OBJECT_KIND (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
-insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
-insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
-insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
-
-
----------------------------------
--- Address Book Object Members --
----------------------------------
-
-create table ABO_MEMBERS (
- GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
- ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
- MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
-
- primary key (GROUP_ID, MEMBER_ID) -- implicit index
-);
-
-create index ABO_MEMBERS_ADDRESSBOOK_ID on
- ABO_MEMBERS(ADDRESSBOOK_ID);
-create index ABO_MEMBERS_MEMBER_ID on
- ABO_MEMBERS(MEMBER_ID);
-
-------------------------------------------
--- Address Book Object Foreign Members --
-------------------------------------------
-
-create table ABO_FOREIGN_MEMBERS (
- GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
- ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
- MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
-
- primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
-);
-
-create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
- ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
-
------------------------
--- Shared Group Bind --
------------------------
-
--- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
-
-create table SHARED_GROUP_BIND (
- ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
- GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
- BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
- BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
- BIND_REVISION integer default 0 not null,
- MESSAGE text, -- FIXME: xml?
-
- primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
-);
-
-create index SHARED_GROUP_BIND_RESOURCE_ID on
- SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
-
-
----------------
--- Revisions --
----------------
-
-create sequence REVISION_SEQ;
-
-
--------------------------------
--- Calendar Object Revisions --
--------------------------------
-
-create table CALENDAR_OBJECT_REVISIONS (
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- CALENDAR_RESOURCE_ID integer references CALENDAR,
- CALENDAR_NAME varchar(255) default null,
- RESOURCE_NAME varchar(255),
- REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null
-);
-
-create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
-
-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
-
-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
-
-
-----------------------------------
--- AddressBook Object Revisions --
-----------------------------------
-
-create table ADDRESSBOOK_OBJECT_REVISIONS (
- ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
- ADDRESSBOOK_NAME varchar(255) default null,
- RESOURCE_NAME varchar(255),
- REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null
-);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
- on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
-
-
------------------------------------
--- Notification Object Revisions --
------------------------------------
-
-create table NOTIFICATION_OBJECT_REVISIONS (
- NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
- RESOURCE_NAME varchar(255),
- REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null,
-
- unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
-);
-
-create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
- on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
-
-
--------------------------------------------
--- Apple Push Notification Subscriptions --
--------------------------------------------
-
-create table APN_SUBSCRIPTIONS (
- TOKEN varchar(255) not null,
- RESOURCE_KEY varchar(255) not null,
- MODIFIED integer not null,
- SUBSCRIBER_GUID varchar(255) not null,
- USER_AGENT varchar(255) default null,
- IP_ADDR varchar(255) default null,
-
- primary key (TOKEN, RESOURCE_KEY) -- implicit index
-);
-
-create index APN_SUBSCRIPTIONS_RESOURCE_KEY
- on APN_SUBSCRIPTIONS(RESOURCE_KEY);
-
-
------------------
--- IMIP Tokens --
------------------
-
-create table IMIP_TOKENS (
- TOKEN varchar(255) not null,
- ORGANIZER varchar(255) not null,
- ATTENDEE varchar(255) not null,
- ICALUID varchar(255) not null,
- ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
-);
-
-create index IMIP_TOKENS_TOKEN
- on IMIP_TOKENS(TOKEN);
-
-
-----------------
--- Work Items --
-----------------
-
-create sequence WORKITEM_SEQ;
-
-
----------------------------
--- IMIP Inivitation Work --
----------------------------
-
-create table IMIP_INVITATION_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- FROM_ADDR varchar(255) not null,
- TO_ADDR varchar(255) not null,
- ICALENDAR_TEXT text not null
-);
-
-
------------------------
--- IMIP Polling Work --
------------------------
-
-create table IMIP_POLLING_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
----------------------
--- IMIP Reply Work --
----------------------
-
-create table IMIP_REPLY_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- ORGANIZER varchar(255) not null,
- ATTENDEE varchar(255) not null,
- ICALENDAR_TEXT text not null
-);
-
-
-------------------------
--- Push Notifications --
-------------------------
-
-create table PUSH_NOTIFICATION_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PUSH_ID varchar(255) not null
-);
-
------------------
--- GroupCacher --
------------------
-
-create table GROUP_CACHER_POLLING_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
---------------------------
--- Object Splitter Work --
---------------------------
-
-create table CALENDAR_OBJECT_SPLITTER_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
-);
-
-create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
- CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
-
---------------------
--- Schema Version --
---------------------
-
-create table CALENDARSERVER (
- NAME varchar(255) primary key, -- implicit index
- VALUE varchar(255)
-);
-
-insert into CALENDARSERVER values ('VERSION', '24');
-insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
-insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,700 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+ CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+ CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+ CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+ ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+ ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+ ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+ ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+ CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '24');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,700 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+ CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+ CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+ CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+ ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+ ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+ ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+ ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+ CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '26');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,703 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+ CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+ CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+ CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+ CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+ ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+ ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+ ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+ ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+ CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '27');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,704 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+ CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+ CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+ CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+ CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+ ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+ ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+ ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+ ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null,
+ PRIORITY integer not null -- 1:low 5:medium 10:high
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+ CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '28');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,26 +0,0 @@
-----
--- Copyright (c) 2012-2013 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.
-----
-
----------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
----------------------------------------------------
-
--- This is actually a noop for Oracle as we had some invalid names in the v20 schema that
--- were corrected in v20 (but not corrected in postgres which is being updated for v25).
-
--- Now update the version
--- No data upgrades
-update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,26 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- This is actually a noop for Oracle as we had some invalid names in the v20 schema that
+-- were corrected in v20 (but not corrected in postgres which is being updated for v25).
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,35 +15,29 @@
----
---------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
+-- Upgrade database schema from VERSION 25 to 26 --
---------------------------------------------------
-----------------------------------------
--- Change Address Book Object Members --
-----------------------------------------
+-- Replace index
-alter table ABO_MEMBERS
- drop ("abo_members_member_id_fkey");
-alter table ABO_MEMBERS
- drop ("abo_members_group_id_fkey");
-alter table ABO_MEMBERS
- add ("REVISION" integer default nextval('REVISION_SEQ') not null);
-alter table ABO_MEMBERS
- add ("REMOVED" boolean default false not null);
-alter table ABO_MEMBERS
- drop ("abo_members_pkey");
-alter table ABO_MEMBERS
- add ("abo_members_pkey" primary key ("GROUP_ID", "MEMBER_ID", "REVISION"));
+drop index CALENDAR_OBJECT_REVIS_2643d556;
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
-------------------------------------------
--- Change Address Book Object Revisions --
-------------------------------------------
-
-alter table ADDRESSBOOK_OBJECT_REVISIONS
- add ("OBJECT_RESOURCE_ID" integer default 0);
---------------------
--- Update version --
---------------------
+drop index ADDRESSBOOK_OBJECT_RE_980b9872;
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+-- Now update the version
+-- No data upgrades
update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,33 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add ("DEFAULT_POLLS" integer default null references CALENDAR on delete set null);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,29 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 27 to 28 --
+---------------------------------------------------
+
+-- Push notification work related updates
+
+alter table PUSH_NOTIFICATION_WORK
+ add ("PRIORITY" integer default 10 not null);
+
+update PUSH_NOTIFICATION_WORK set PRIORITY = 10;
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '28' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,49 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 28 to 29 --
+---------------------------------------------------
+
+----------------------------------------
+-- Change Address Book Object Members --
+----------------------------------------
+
+alter table ABO_MEMBERS
+ drop ("abo_members_member_id_fkey");
+alter table ABO_MEMBERS
+ drop ("abo_members_group_id_fkey");
+alter table ABO_MEMBERS
+ add ("REVISION" integer default nextval('REVISION_SEQ') not null);
+alter table ABO_MEMBERS
+ add ("REMOVED" boolean default false not null);
+alter table ABO_MEMBERS
+ drop ("abo_members_pkey");
+alter table ABO_MEMBERS
+ add ("abo_members_pkey" primary key ("GROUP_ID", "MEMBER_ID", "REVISION"));
+
+------------------------------------------
+-- Change Address Book Object Revisions --
+------------------------------------------
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ add ("OBJECT_RESOURCE_ID" integer default 0);
+
+--------------------
+-- Update version --
+--------------------
+
+update CALENDARSERVER set VALUE = '29' where NAME = 'VERSION';
Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -1,35 +0,0 @@
-----
--- Copyright (c) 2012-2013 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.
-----
-
----------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
----------------------------------------------------
-
--- Rename columns and indexes
-alter table SHARED_ADDRESSBOOK_BIND
- rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
-
-alter table SHARED_GROUP_BIND
- rename column GROUP_ADDRESSBOOK_RESOURCE_NAME to GROUP_ADDRESSBOOK_NAME;
-
-alter table ADDRESSBOOK_OBJECT_REVISIONS
- rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
-
-alter index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID rename to ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID;
-
--- Now update the version
--- No data upgrades
-update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,35 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- Rename columns and indexes
+alter table SHARED_ADDRESSBOOK_BIND
+ rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter table SHARED_GROUP_BIND
+ rename column GROUP_ADDRESSBOOK_RESOURCE_NAME to GROUP_ADDRESSBOOK_NAME;
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID rename to ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -15,30 +15,18 @@
----
---------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
+-- Upgrade database schema from VERSION 25 to 26 --
---------------------------------------------------
-----------------------------------------
--- Change Address Book Object Members --
-----------------------------------------
+-- Replace index
+drop index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME;
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
-alter table ABO_MEMBERS
- drop constraint abo_members_member_id_fkey,
- drop constraint abo_members_group_id_fkey,
- add column REVISION integer default nextval('REVISION_SEQ') not null,
- add column REMOVED boolean default false not null,
- drop constraint abo_members_pkey,
- add constraint abo_members_pkey primary key(GROUP_ID, MEMBER_ID, REVISION);
+drop index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME;
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
-------------------------------------------
--- Change Address Book Object Revisions --
-------------------------------------------
-
-alter table ADDRESSBOOK_OBJECT_REVISIONS
- add column OBJECT_RESOURCE_ID integer default 0;
-
---------------------
--- Update version --
---------------------
-
+-- Now update the version
+-- No data upgrades
update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,31 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add column DEFAULT_POLLS integer default null references CALENDAR on delete set null;
+
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+ CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,29 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 27 to 28 --
+---------------------------------------------------
+
+-- Push notification work related updates
+
+alter table PUSH_NOTIFICATION_WORK
+ add column PRIORITY integer default 10 not null;
+
+update PUSH_NOTIFICATION_WORK set PRIORITY = 10;
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '28' where NAME = 'VERSION';
Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql 2013-12-14 06:28:16 UTC (rev 12110)
@@ -0,0 +1,44 @@
+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 28 to 29 --
+---------------------------------------------------
+
+----------------------------------------
+-- Change Address Book Object Members --
+----------------------------------------
+
+alter table ABO_MEMBERS
+ drop constraint abo_members_member_id_fkey,
+ drop constraint abo_members_group_id_fkey,
+ add column REVISION integer default nextval('REVISION_SEQ') not null,
+ add column REMOVED boolean default false not null,
+ drop constraint abo_members_pkey,
+ add constraint abo_members_pkey primary key(GROUP_ID, MEMBER_ID, REVISION);
+
+------------------------------------------
+-- Change Address Book Object Revisions --
+------------------------------------------
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+ add column OBJECT_RESOURCE_ID integer default 0;
+
+--------------------
+-- Update version --
+--------------------
+
+update CALENDARSERVER set VALUE = '29' where NAME = 'VERSION';
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -32,7 +32,7 @@
from hashlib import md5
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
from random import Random
@@ -50,6 +50,7 @@
from twisted.internet.task import deferLater
from twisted.trial.unittest import TestCase
+from twistedcaldav import ical
from twistedcaldav.config import config
from twistedcaldav.stdconfig import DEFAULT_CONFIG
from twistedcaldav.vcard import Component as ABComponent
@@ -447,12 +448,11 @@
# We don't want the default calendar or inbox to appear unless it's
# explicitly listed.
try:
- yield home.removeCalendarWithName("calendar")
- # FIXME: this should be an argument to the function, not a
- # global configuration variable. Related: this needs
- # independent tests.
if config.RestrictCalendarsToOneComponentType:
- yield home.removeCalendarWithName("tasks")
+ for name in ical.allowedStoreComponents:
+ yield home.removeCalendarWithName(home._componentCalendarName[name])
+ else:
+ yield home.removeCalendarWithName("calendar")
yield home.removeCalendarWithName("inbox")
except NoSuchHomeChildError:
pass
@@ -480,7 +480,7 @@
Update the supplied iCalendar data so that all dates are updated to the current year.
"""
- nowYear = PyCalendarDateTime.getToday().getYear()
+ nowYear = DateTime.getToday().getYear()
return data % {"now": nowYear}
@@ -726,8 +726,8 @@
return "/%s/%s/%s/" % (prefix, self.hostname, id)
- def send(self, prefix, id, txn):
- self.history.append(self.pushKeyForId(prefix, id))
+ def send(self, prefix, id, txn, priority):
+ self.history.append((self.pushKeyForId(prefix, id), priority))
def reset(self):
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -94,27 +94,32 @@
"""
inbox = (yield home.calendarWithName("inbox"))
- prop = inbox.properties().get(PropertyName.fromElement(propname))
- if prop is not None:
- defaultCalendar = str(prop.children[0])
- parts = defaultCalendar.split("/")
- if len(parts) == 5:
+ if inbox is not None:
+ prop = inbox.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ defaultCalendar = str(prop.children[0])
+ parts = defaultCalendar.split("/")
+ if len(parts) == 5:
- calendarName = parts[-1]
- calendarHomeUID = parts[-2]
- if calendarHomeUID == home.uid():
+ calendarName = parts[-1]
+ calendarHomeUID = parts[-2]
+ if calendarHomeUID == home.uid():
- calendar = (yield home.calendarWithName(calendarName))
- if calendar is not None:
- try:
- yield home.setDefaultCalendar(
- calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
- )
- except InvalidDefaultCalendar:
- # Ignore these - the server will recover
- pass
+ calendar = (yield home.calendarWithName(calendarName))
+ if calendar is not None:
+ try:
+ if propname == caldavxml.ScheduleDefaultCalendarURL:
+ ctype = "VEVENT"
+ elif propname == customxml.ScheduleDefaultTasksURL:
+ ctype = "VTODO"
+ yield home.setDefaultCalendar(
+ calendar, ctype
+ )
+ except InvalidDefaultCalendar:
+ # Ignore these - the server will recover
+ pass
- del inbox.properties()[PropertyName.fromElement(propname)]
+ del inbox.properties()[PropertyName.fromElement(propname)]
@@ -130,15 +135,13 @@
calendars = (yield home.loadChildren())
for calendar in calendars:
if calendar.isInbox():
- continue
+ prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+ if prop is not None:
+ del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
prop = calendar.properties().get(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp))
if prop is not None:
yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
del calendar.properties()[PropertyName.fromElement(caldavxml.ScheduleCalendarTransp)]
- inbox = (yield home.calendarWithName("inbox"))
- prop = inbox.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
- if prop is not None:
- del inbox.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -102,10 +102,11 @@
the new value from the XML property.
"""
inbox = (yield home.calendarWithName("inbox"))
- prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
- if prop is not None:
- yield home.setAvailability(prop.calendar())
- del inbox.properties()[customxml.CalendarAvailability]
+ if inbox is not None:
+ prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
+ if prop is not None:
+ yield home.setAvailability(prop.calendar())
+ del inbox.properties()[PropertyName.fromElement(customxml.CalendarAvailability)]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -161,7 +161,10 @@
self.assertEqual(version, 4)
calendar = (yield self.calendarUnderTest(name=calname, home=user))
self.assertEqual(calendar.getTimezone(), None)
- self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
+ if tz:
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
+ else:
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
@inlineCallbacks
@@ -272,7 +275,10 @@
self.assertEqual(version, 4)
calendar = (yield self.calendarUnderTest(name="inbox", home=user))
self.assertEqual(home.getAvailability(), None)
- self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
+ if av:
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
+ else:
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
@inlineCallbacks
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py 2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py 2013-12-14 06:28:16 UTC (rev 12110)
@@ -34,6 +34,9 @@
from zope.interface import Attribute, Interface
from zope.interface.common.mapping import IMapping
+from twisted.python.constants import Values, ValueConstant
+from calendarserver.push.util import PushPriority
+
#
# Exceptions
#
@@ -231,6 +234,19 @@
+class ChangeCategory(Values):
+ """
+ Constants to use for notifyChanged's category parameter. Maps
+ types of changes to the appropriate push priority level.
+ TODO: make these values configurable in plist perhaps.
+ """
+ default = ValueConstant(PushPriority.high)
+ inbox = ValueConstant(PushPriority.medium)
+ attendeeITIPUpdate = ValueConstant(PushPriority.medium)
+ organizerITIPUpdate = ValueConstant(PushPriority.medium)
+
+
+
class INotifier(Interface):
"""
Interface for an object that can send change notifications. Notifiers are associated with specific notifier factories
@@ -260,9 +276,12 @@
@rtype: L{IStoreNotifier} or C{None}
"""
- def notifyChanged(): #@NoSelf
+ def notifyChanged(category): #@NoSelf
"""
Send a change notification to any notifiers assigned to the object.
+
+ @param category: the kind of change triggering this notification
+ @type: L{ChangeCategory}
"""
def notifierID(): #@NoSelf
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/bb7628ed/attachment.html>
More information about the calendarserver-changes
mailing list