Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

checkpoint

  • Loading branch information
bgaya committed Dec 7, 2013
1 parent f4029ae commit 20c3e2382fff4fcd78e1abc693a4cc89b1327c9a
Showing with 104 additions and 57 deletions.
  1. +15 −47 twext/who/groups.py
  2. +1 −1 twext/who/test/test_groups.py
  3. +46 −3 twistedcaldav/ical.py
  4. +39 −4 txdav/caldav/datastore/sql.py
  5. +3 −2 txdav/caldav/datastore/util.py
@@ -25,7 +25,7 @@
from twext.who.delegates import allGroupDelegates
from twext.who.idirectory import RecordType
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.ical import ignoredComponents
from txdav.caldav.datastore.util import normalizationLookup
from txdav.common.datastore.sql_tables import schema
import datetime
import hashlib
@@ -133,15 +133,6 @@ def doWork(self):
)
).on(self.transaction)

# get group individual UIDs
groupMemember = schema.GROUP_MEMBERSHIP
rows = yield Select(
[groupMemember.MEMBER_GUID, ],
From=groupMemember,
Where=groupMemember.GROUP_ID == self.groupID,
).on(self.transaction)
individualGUIDs = [row[0] for row in rows]

# get calendar Object
calObject = schema.CALENDAR_OBJECT
rows = yield Select(
@@ -150,7 +141,7 @@ def doWork(self):
Where=calObject.RESOURCE_ID == self.eventID,
).on(self.transaction)

calendarID = row[0][0]
calendarID = rows[0][0]
calendarHome = (yield self.Calendar._ownerHomeWithResourceID.on(
self.transaction, resourceID=calendarID)
)[0][0]
@@ -159,43 +150,20 @@ def doWork(self):
calendarObject = yield calendar.objectResourceWithID(self.eventID)
changed = False

individualUUIDs = set(["urn:uuid:" + individualGUID for individualGUID in individualGUIDs])
groupUUID = "urn:uuid:" + self.groupGUID()
vcalendar = yield calendarObject.component()
for component in vcalendar.subcomponents():
if component.name() in ignoredComponents:
continue

oldAttendeeProps = component.getAttendees()
oldAttendeeUUIDs = set([attendeeProp.value() for attendeeProp in oldAttendeeProps])

# add new member attendees
for individualUUID in individualUUIDs - oldAttendeeUUIDs:
individualGUID = individualUUID[len("urn:uuid:"):]
directoryRecord = self.transaction.directoryService().recordWithUID(individualGUID)
newAttendeeProp = directoryRecord.attendee(params={"MEMBER": groupUUID})
component.addProperty(newAttendeeProp)
changed = True

# remove attendee or update MEMBER attribute for non-primary attendees in this group,
for attendeeProp in oldAttendeeProps:
if attendeeProp.hasParameter("MEMBER"):
parameterValues = attendeeProp.parameterValues("MEMBER")
if groupUUID in parameterValues:
if attendeeProp.value() not in individualUUIDs:
attendeeProp.removeParameterValue("MEMBER", groupUUID)
if not attendeeProp.parameterValues("MEMBER"):
component.removeProperty(attendeeProp)
changed = True
else:
if attendeeProp.value() in individualUUIDs:
attendeeProp.setParameter("MEMBER", parameterValues + [groupUUID, ])
changed = True

# replace old with new
# get group individual UIDs
groupMemember = schema.GROUP_MEMBERSHIP
rows = yield Select(
[groupMemember.MEMBER_GUID, ],
From=groupMemember,
Where=groupMemember.GROUP_ID == self.groupID,
).on(self.transaction)
individualGUIDs = [row[0] for row in rows]

component = yield calendarObject.component()
changed = component.expandGroupAttendee(self.groupGUID, individualGUIDs, normalizationLookup, self.directoryService().recordWithCalendarUserAddress)

if changed:
# TODO: call calendarObject._setComponentInternal( vcalendar, mode ) instead?
yield calendarObject.setComponent(vcalendar)
yield calendarObject.setComponent(component)



@@ -343,7 +343,7 @@ def test_groupAttendeeReconciliation(self):
DURATION:PT1H
ATTENDEE;CN=User 01;EMAIL=user01@example.com;RSVP=TRUE:urn:uuid:user01
ATTENDEE;CN=User 02;EMAIL=user02@example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
ATTENDEE;CN=Group 01;EMAIL=group01@example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:uuid:group01
ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01@example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:uuid:group01
CREATED:20060101T150000Z
ORGANIZER;CN=User 01;EMAIL=user01@example.com:urn:uuid:user01
SUMMARY:event 1
@@ -3251,7 +3251,7 @@ def normalizeCalendarUserAddresses(self, lookupFunction, principalFunction,
# Check that we can lookup this calendar user address - if not
# we cannot do anything with it
cuaddr = normalizeCUAddr(prop.value())
name, guid, cuaddrs = lookupFunction(cuaddr, principalFunction, config)
name, guid, cutype, cuaddrs = lookupFunction(cuaddr, principalFunction, config)
if guid is None:
continue

@@ -3263,8 +3263,6 @@ def normalizeCalendarUserAddresses(self, lookupFunction, principalFunction,
# Get any CN parameter
oldCN = prop.parameterValue("CN")

cutype = prop.parameterValue("CUTYPE")

if toUUID:
# Always re-write value to urn:uuid
prop.setValue("urn:uuid:%s" % (guid,))
@@ -3343,11 +3341,56 @@ def normalizeCalendarUserAddresses(self, lookupFunction, principalFunction,
else:
prop.removeParameter("EMAIL")

if cutype == "INDIVIDUAL":
cutype = None

if cutype != prop.parameterValue("CUTYPE"):
if cutype:
prop.setParameter("CUTYPE", cutype)
else:
prop.removeParameter("CUTYPE")

# For VPOLL also do immediate children
if component.name() == "VPOLL":
component.normalizeCalendarUserAddresses(lookupFunction, principalFunction, toUUID)


def expandGroupAttendee(self, groupGUID, individualGUIDs, lookupFunction, principalFunction):

individualUUIDs = set(["urn:uuid:" + individualGUID for individualGUID in individualGUIDs])
groupUUID = "urn:uuid:" + groupGUID
changed = False
for component in self.subcomponents():
if component.name() in ignoredComponents:
continue

oldAttendeeProps = component.properties("ATTENDEE")
oldAttendeeUUIDs = set([attendeeProp.value() for attendeeProp in oldAttendeeProps])

# add new member attendees
for individualUUID in individualUUIDs - oldAttendeeUUIDs:
directoryRecord = lookupFunction(individualUUID, principalFunction, config)
newAttendeeProp = directoryRecord.attendee(params={"MEMBER": groupUUID})
component.addProperty(newAttendeeProp)
changed = True

# remove attendee or update MEMBER attribute for non-primary attendees in this group,
for attendeeProp in oldAttendeeProps:
if attendeeProp.hasParameter("MEMBER"):
parameterValues = attendeeProp.parameterValues("MEMBER")
if groupUUID in parameterValues:
if attendeeProp.value() not in individualUUIDs:
attendeeProp.removeParameterValue("MEMBER", groupUUID)
if not attendeeProp.parameterValues("MEMBER"):
component.removeProperty(attendeeProp)
changed = True
else:
if attendeeProp.value() in individualUUIDs:
attendeeProp.setParameter("MEMBER", parameterValues + [groupUUID, ])
changed = True
return changed


def allPerUserUIDs(self):

results = set()
@@ -1582,13 +1582,16 @@ def fullValidation(self, component, inserting, internal_state):
if not self.calendar().isSupportedComponent(component.mainType()):
raise InvalidComponentTypeError("Invalid component type %s for calendar: %s" % (component.mainType(), self.calendar(),))

# Valid attendee list size check
yield self.validAttendeeListSizeCheck(component, inserting)

# Normalize the calendar user addresses once we know we have valid
# calendar data
component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)

# Expand groups
yield self.expandGroupAttendees(component)

# Valid attendee list size check
yield self.validAttendeeListSizeCheck(component, inserting)

# Possible timezone stripping
if config.EnableTimezonesByReference:
component.stripKnownTimezones()
@@ -1601,7 +1604,39 @@ def fullValidation(self, component, inserting, internal_state):
self.validAccess(component, inserting, internal_state)


def validCalendarDataCheck(self, component, inserting):
@inlineCallbacks
def expandGroupAttendees(self, component):
"""
Expand group attendees
"""

if not config.Scheduling.Options.AllowGroupAsAttendee:
return

attendeeProps = component.getAllAttendeeProperties()
groupGUIDs = [
attendeeProp.value()[len("urn:uuid:"):] for attendeeProp in attendeeProps
if attendeeProp.parameterValue("CUTYPE") == "GROUP"
]

# FIXME: need to add event to Group resource ID here
# need get get members here because they may not be cached yet

for groupGUID in groupGUIDs:
groupID, name, membershipHash = yield self._txn.groupByGUID(groupGUID)

groupMemember = schema.GROUP_MEMBERSHIP
rows = yield Select(
[groupMemember.MEMBER_GUID, ],
From=groupMemember,
Where=groupMemember.GROUP_ID == groupID,
).on(self._txn)
individualGUIDs = [row[0] for row in rows]

component.expandGroupAttendee(groupGUID, individualGUIDs, normalizationLookup, self.directoryService().recordWithCalendarUserAddress)


def validCalendarDataCheck(self, component, inserting): #@UnusedVariable
"""
Check that the calendar data is valid iCalendar.
@return: tuple: (True/False if the calendar data is valid,
@@ -103,7 +103,7 @@ def validateCalendarComponent(calendarObject, calendar, component, inserting, mi
def normalizationLookup(cuaddr, recordFunction, config):
"""
Lookup function to be passed to ical.normalizeCalendarUserAddresses.
Returns a tuple of (Full name, guid, and calendar user address list)
Returns a tuple of (Full name, guid, cutype and calendar user address list)
for the given cuaddr. The recordFunction is called to retrieve the
record for the cuaddr.
"""
@@ -114,7 +114,7 @@ def normalizationLookup(cuaddr, recordFunction, config):
record = None

if record is None:
return (None, None, None)
return (None, None, None, None)
else:
# RFC5545 syntax does not allow backslash escaping in
# parameter values. A double-quote is thus not allowed
@@ -124,6 +124,7 @@ def normalizationLookup(cuaddr, recordFunction, config):
return (
record.fullName.replace('"', "'"),
record.uid,
record.getCUType(),
record.calendarUserAddresses,
)

0 comments on commit 20c3e23

Please sign in to comment.
You can’t perform that action at this time.