diff --git a/ChangeLog b/ChangeLog index 7481b84e1..e4d4b05e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2008-07-09 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoAppointmentFolders.m ([SOGoAppointmentFolders -davCreateCalendarCollection:newNameinContext:createContext]): + new method that execute a "MKCALENDAR" request. + + * SoObjects/SOGo/SOGoObject.m (SOGoSelectorForPropertyGetter): new + function that returns the selector for the getter method for the + specified property. + (SOGoSelectorForPropertySetter): same as above for the setter + method. + + * SoObjects/SOGo/WORequest+SOGo.m ([WORequest + -davPatchedPropertiesWithTopTag:topTag]): new category method to + help with parsing properties modified or removed from PROPPATCH + methods and alike. + * SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject -PUTAction:_ctx]): increment the version number only if the record is not new. diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index f0fffbcba..d2e6754f1 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -891,36 +891,6 @@ static Class sogoAppointmentFolderKlass = Nil; #warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:) #warning components in calendar-data query are ignored -static inline SEL -_selectorForProperty (NSString *property) -{ - static NSMutableDictionary *methodMap = nil; - SEL propSel; - NSValue *propPtr; - NSDictionary *map; - NSString *methodName; - - if (!methodMap) - methodMap = [NSMutableDictionary new]; - propPtr = [methodMap objectForKey: property]; - if (propPtr) - propSel = [propPtr pointerValue]; - else - { - map = [sogoAppointmentFolderKlass defaultWebDAVAttributeMap]; - methodName = [map objectForKey: property]; - if (methodName) - { - propSel = NSSelectorFromString (methodName); - if (propSel) - [methodMap setObject: [NSValue valueWithPointer: propSel] - forKey: property]; - } - } - - return propSel; -} - - (NSString *) _nodeTagForProperty: (NSString *) property { NSString *namespace, *nodeName, *nsRep; @@ -984,7 +954,7 @@ _selectorForProperty (NSString *property) currentValue = values; while (*currentProperty) { - methodSel = _selectorForProperty (*currentProperty); + methodSel = SOGoSelectorForPropertyGetter (*currentProperty); if (methodSel && [sogoObject respondsToSelector: methodSel]) *currentValue = [[sogoObject performSelector: methodSel] stringByEscapingXMLString]; diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index d0dd127b9..c12ab6135 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -21,12 +21,14 @@ */ #import +#import #import #import #import #import +#import #import "SOGoAppointmentFolder.h" #import "SOGoAppointmentFolders.h" @@ -48,40 +50,104 @@ return [self labelForKey: @"Personal Calendar"]; } -- (id) lookupName: (NSString *) name - inContext: (WOContext *) lookupContext - acquire: (BOOL) acquire +- (NSString *) _fetchPropertyWithName: (NSString *) propertyName + inArray: (NSArray *) section { - id obj; - WORequest *rq; + NSObject *currentElement; + NSString *currentName, *property; + NSEnumerator *elements; + NSObject *values; - obj = [super lookupName: name inContext: lookupContext acquire: NO]; + property = nil; - rq = [context request]; - if ([rq isSoWebDAVRequest] - && [[rq method] isEqualToString: @"MKCALENDAR"]) + elements = [section objectEnumerator]; + while (!property && (currentElement = [elements nextObject])) { - if (obj) - obj = [NSException exceptionWithHTTPStatus: 403]; - else + currentName = [NSString stringWithFormat: @"{%@}%@", + [currentElement namespaceURI], + [currentElement nodeName]]; + if ([currentName isEqualToString: propertyName]) { - obj = [self newFolderWithName: name andNameInContainer: name]; - if (!obj) - obj = [super lookupName: name inContext: lookupContext acquire: NO]; + values = [currentElement childNodes]; + if ([values length]) + property = [[values objectAtIndex: 0] nodeValue]; } } - return obj; + return property; } -- (id) doMKCALENDAR: (id) test +#warning this method may be useful at a higher level +#warning not all values are simple strings... +- (NSException *) _applyMkCalendarProperties: (NSArray *) properties + toObject: (SOGoObject *) newFolder { + NSEnumerator *allProperties; + NSObject *currentProperty; + NSObject *values; + NSString *value, *currentName; + SEL methodSel; + + allProperties = [properties objectEnumerator]; + while ((currentProperty = [allProperties nextObject])) + { + values = [currentProperty childNodes]; + if ([values length]) + { + value = [[values objectAtIndex: 0] nodeValue]; + currentName = [NSString stringWithFormat: @"{%@}%@", + [currentProperty namespaceURI], + [currentProperty nodeName]]; + methodSel = SOGoSelectorForPropertySetter (currentName); + if ([newFolder respondsToSelector: methodSel]) + [newFolder performSelector: methodSel + withObject: value]; + } + } + return nil; } -- (id) MKCALENDARAction: (id) localContext +- (NSException *) davCreateCalendarCollection: (NSString *) newName + inContext: (id) createContext { - return nil; + NSArray *subfolderNames, *setProperties; + NSString *content, *newDisplayName; + NSDictionary *properties; + NSException *error; + SOGoAppointmentFolder *newFolder; + + subfolderNames = [self toManyRelationshipKeys]; + if ([subfolderNames containsObject: newName]) + { + content = [NSString stringWithFormat: + @"A collection named '%@' already exists.", + newName]; + error = [NSException exceptionWithHTTPStatus: 403 + reason: content]; + } + else + { + properties = [[createContext request] + davPatchedPropertiesWithTopTag: @"mkcalendar"]; + setProperties = [properties objectForKey: @"set"]; + newDisplayName = [self _fetchPropertyWithName: @"{DAV:}displayname" + inArray: setProperties]; + if (![newDisplayName length]) + newDisplayName = newName; + error + = [self newFolderWithName: newDisplayName andNameInContainer: newName]; + if (!error) + { + newFolder = [self lookupName: newName + inContext: createContext + acquire: NO]; + error = [self _applyMkCalendarProperties: setProperties + toObject: newFolder]; + } + } + + return error; } #warning THIS CAUSES LIGHTNING TO FAIL (that is why its commented out) diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 4dc554da9..afaf8ad00 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -62,6 +62,9 @@ #define $(class) NSClassFromString(class) +SEL SOGoSelectorForPropertyGetter (NSString *property); +SEL SOGoSelectorForPropertySetter (NSString *property); + @interface SOGoObject : NSObject { WOContext *context; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 5d611b5b7..b8bb2af17 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -71,6 +71,64 @@ static BOOL kontactGroupDAV = YES; static BOOL sendACLAdvisories = NO; static NSDictionary *reportMap = nil; +static NSMutableDictionary *setterMap = nil; +static NSMutableDictionary *getterMap = nil; + +SEL SOGoSelectorForPropertyGetter (NSString *property) +{ + SEL propSel; + NSValue *propPtr; + NSDictionary *map; + NSString *methodName; + + if (!getterMap) + getterMap = [NSMutableDictionary new]; + propPtr = [getterMap objectForKey: property]; + if (propPtr) + propSel = [propPtr pointerValue]; + else + { + map = [SOGoObject defaultWebDAVAttributeMap]; + methodName = [map objectForKey: property]; + if (methodName) + { + propSel = NSSelectorFromString (methodName); + if (propSel) + [getterMap setObject: [NSValue valueWithPointer: propSel] + forKey: property]; + } + } + + return propSel; +} + +SEL SOGoSelectorForPropertySetter (NSString *property) +{ + SEL propSel; + NSValue *propPtr; + NSDictionary *map; + NSString *methodName; + + if (!setterMap) + setterMap = [NSMutableDictionary new]; + propPtr = [setterMap objectForKey: property]; + if (propPtr) + propSel = [propPtr pointerValue]; + else + { + map = [SOGoObject defaultWebDAVAttributeMap]; + methodName = [map objectForKey: property]; + if (methodName) + { + propSel = NSSelectorFromString ([methodName davSetterName]); + if (propSel) + [setterMap setObject: [NSValue valueWithPointer: propSel] + forKey: property]; + } + } + + return propSel; +} @implementation SOGoObject diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index 3001e6b3b..4271862e3 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -43,6 +43,7 @@ - (void) setBaseOCSPath: (NSString *) newOCSPath; +- (NSArray *) toManyRelationshipKeys; - (NSArray *) subFolders; - (NSException *) newFolderWithName: (NSString *) name diff --git a/SoObjects/SOGo/WORequest+SOGo.h b/SoObjects/SOGo/WORequest+SOGo.h index 647f0de34..ac6cc1065 100644 --- a/SoObjects/SOGo/WORequest+SOGo.h +++ b/SoObjects/SOGo/WORequest+SOGo.h @@ -28,6 +28,7 @@ @interface WORequest (SOGoSOPEUtilities) - (BOOL) handledByDefaultHandler; +- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag; @end diff --git a/SoObjects/SOGo/WORequest+SOGo.m b/SoObjects/SOGo/WORequest+SOGo.m index 776cacf9d..eea2fae5e 100644 --- a/SoObjects/SOGo/WORequest+SOGo.m +++ b/SoObjects/SOGo/WORequest+SOGo.m @@ -20,9 +20,13 @@ * Boston, MA 02111-1307, USA. */ +#import + #import #import +#import + #import "WORequest+SOGo.h" @implementation WORequest (SOGoSOPEUtilities) @@ -33,4 +37,68 @@ return (![requestHandlerKey isEqualToString: @"dav"]); } +- (NSArray *) _propertiesOfElement: (id ) startElement + underTag: (NSString *) tag +{ + id list; + id tagElement; + NSObject *currentNode; + NSMutableArray *properties; + unsigned int count, max; + + properties = nil; + + list = [startElement getElementsByTagName: tag]; + if ([list length]) + { + tagElement = [list objectAtIndex: 0]; + list = [tagElement getElementsByTagName: @"prop"]; + if ([list length]) + { + tagElement = [list objectAtIndex: 0]; + properties = [NSMutableArray array]; + list = [tagElement childNodes]; + max = [list length]; + for (count = 0; count < max; count++) + { + currentNode = [list objectAtIndex: count]; + if ([currentNode conformsToProtocol: @protocol (DOMElement)]) + [properties addObject: currentNode]; + } + } + } + + return properties; +} + +- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag +{ + NSMutableDictionary *patchedProperties; + NSArray *props; + id element; + id startElement; + NSObject *list; + + patchedProperties = nil; + if (!topTag) + topTag = @"propertyupdate"; + element = [self contentAsDOMDocument]; + list = [element getElementsByTagName: topTag]; + if ([list length]) + { + startElement = [list objectAtIndex: 0]; + patchedProperties = [NSMutableDictionary dictionary]; + props = [self _propertiesOfElement: startElement + underTag: @"set"]; + if (props) + [patchedProperties setObject: props forKey: @"set"]; + props = [self _propertiesOfElement: startElement + underTag: @"remove"]; + if (props) + [patchedProperties setObject: props forKey: @"remove"]; + } + + return patchedProperties; +} + @end