2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #import <Foundation/NSArray.h>
23 #import <Foundation/NSString.h>
25 #import <NGObjWeb/NSException+HTTP.h>
26 #import <NGObjWeb/SoObject+SoDAV.h>
27 #import <NGObjWeb/WOContext.h>
28 #import <NGObjWeb/WORequest.h>
29 #import <NGObjWeb/WOResponse.h>
30 #import <NGExtensions/NSObject+Logs.h>
31 #import <NGExtensions/NSString+misc.h>
32 #import <EOControl/EOQualifier.h>
33 #import <EOControl/EOSortOrdering.h>
34 #import <GDLContentStore/GCSFolder.h>
36 #import <SoObjects/SOGo/NSDictionary+Utilities.h>
37 #import "SOGoContactGCSEntry.h"
38 #import "SOGoContactGCSFolder.h"
40 #define folderListingFields [NSArray arrayWithObjects: @"c_name", @"c_cn", \
41 @"c_givenname", @"c_sn", @"c_screenname", \
42 @"c_o", @"c_mail", @"c_telephonenumber", \
45 @implementation SOGoContactGCSFolder
49 - (id <SOGoContactObject>) lookupContactWithId: (NSString *) recordId
51 SOGoContactGCSEntry *contact;
53 if ([recordId length] > 0)
54 contact = [SOGoContactGCSEntry objectWithName: recordId
62 - (id) lookupName: (NSString *) _key
63 inContext: (WOContext *) _ctx
70 obj = [super lookupName:_key inContext:_ctx acquire:NO];
74 if ([[[_ctx request] method] isEqualToString: @"PUT"])
76 if ([_key isEqualToString: @"PUT"])
79 obj = [SOGoContactGCSEntry objectWithName: _key
83 obj = [self lookupContactWithId: _key];
85 // if (!(obj || isPut))
86 // obj = [NSException exceptionWithHTTPStatus:404 /* Not Found */];
89 // if ([[self ocsFolder] versionOfContentWithName:_key])
91 // return [self contactWithName:_key inContext:_ctx];
94 /* return 404 to stop acquisition */
99 - (EOQualifier *) _qualifierForFilter: (NSString *) filter
102 EOQualifier *qualifier;
104 if (filter && [filter length] > 0)
106 qs = [NSString stringWithFormat:
107 @"(c_sn isCaseInsensitiveLike: '%@%%') OR "
108 @"(c_givenname isCaseInsensitiveLike: '%@%%') OR "
109 @"(c_mail isCaseInsensitiveLike: '%@%%') OR "
110 @"(c_telephonenumber isCaseInsensitiveLike: '%%%@%%')",
111 filter, filter, filter, filter];
112 qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
120 - (NSArray *) _flattenedRecords: (NSArray *) records
122 NSMutableArray *newRecords;
123 NSEnumerator *oldRecords;
124 NSDictionary *oldRecord;
125 NSMutableDictionary *newRecord;
128 newRecords = [NSMutableArray arrayWithCapacity: [records count]];
130 oldRecords = [records objectEnumerator];
131 oldRecord = [oldRecords nextObject];
134 newRecord = [NSMutableDictionary new];
135 [newRecord autorelease];
137 [newRecord setObject: [oldRecord objectForKey: @"c_name"]
139 [newRecord setObject: [oldRecord objectForKey: @"c_name"]
142 data = [oldRecord objectForKey: @"c_cn"];
144 data = [oldRecord keysWithFormat: @"%{c_givenname} %{c_sn}"];
145 [newRecord setObject: data
146 forKey: @"displayName"];
148 data = [oldRecord objectForKey: @"c_mail"];
151 [newRecord setObject: data forKey: @"mail"];
153 data = [oldRecord objectForKey: @"c_screenname"];
156 [newRecord setObject: data forKey: @"screenName"];
158 data = [oldRecord objectForKey: @"c_o"];
161 [newRecord setObject: data forKey: @"org"];
163 data = [oldRecord objectForKey: @"c_telephonenumber"];
166 [newRecord setObject: data forKey: @"phone"];
168 [newRecords addObject: newRecord];
169 oldRecord = [oldRecords nextObject];
175 - (NSArray *) lookupContactsWithFilter: (NSString *) filter
176 sortBy: (NSString *) sortKey
177 ordering: (NSComparisonResult) sortOrdering
179 NSArray *fields, *dbRecords, *records;
180 EOQualifier *qualifier;
181 EOSortOrdering *ordering;
183 fields = folderListingFields;
184 qualifier = [self _qualifierForFilter: filter];
185 dbRecords = [[self ocsFolder] fetchFields: fields
186 matchingQualifier: qualifier];
188 if ([dbRecords count] > 0)
190 records = [self _flattenedRecords: dbRecords];
192 = [EOSortOrdering sortOrderingWithKey: sortKey
193 selector: ((sortOrdering == NSOrderedDescending)
194 ? EOCompareCaseInsensitiveDescending
195 : EOCompareCaseInsensitiveAscending)];
197 = [records sortedArrayUsingKeyOrderArray:
198 [NSArray arrayWithObject: ordering]];
203 //[self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
205 [self debugWithFormat:@"fetched %i records.", [records count]];
209 #warning this should be unified within SOGoFolder
210 - (void) appendObject: (NSDictionary *) object
211 withBaseURL: (NSString *) baseURL
212 toREPORTResponse: (WOResponse *) r
214 SOGoContactGCSEntry *component;
215 NSString *name, *etagLine, *contactString;
217 name = [object objectForKey: @"c_name"];
218 component = [self lookupName: name inContext: context acquire: NO];
220 [r appendContentString: @" <D:response>\r\n"];
221 [r appendContentString: @" <D:href>"];
222 [r appendContentString: baseURL];
223 if (![baseURL hasSuffix: @"/"])
224 [r appendContentString: @"/"];
225 [r appendContentString: name];
226 [r appendContentString: @"</D:href>\r\n"];
228 [r appendContentString: @" <D:propstat>\r\n"];
229 [r appendContentString: @" <D:prop>\r\n"];
230 etagLine = [NSString stringWithFormat: @" <D:getetag>%@</D:getetag>\r\n",
231 [component davEntityTag]];
232 [r appendContentString: etagLine];
233 [r appendContentString: @" </D:prop>\r\n"];
234 [r appendContentString: @" <D:status>HTTP/1.1 200 OK</D:status>\r\n"];
235 [r appendContentString: @" </D:propstat>\r\n"];
236 [r appendContentString: @" <C:addressbook-data>"];
237 contactString = [[component contentAsString] stringByEscapingXMLString];
238 [r appendContentString: contactString];
239 [r appendContentString: @"</C:addressbook-data>\r\n"];
240 [r appendContentString: @" </D:response>\r\n"];
243 - (NSArray *) davComplianceClassesInContext: (id)_ctx
245 NSMutableArray *classes;
246 NSArray *primaryClasses;
248 classes = [NSMutableArray new];
249 [classes autorelease];
251 primaryClasses = [super davComplianceClassesInContext: _ctx];
253 [classes addObjectsFromArray: primaryClasses];
254 [classes addObject: @"access-control"];
255 [classes addObject: @"addressbook-access"];
260 - (NSArray *) davNamespaces
262 return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"];
265 - (NSString *) groupDavResourceType
267 return @"vcard-collection";
271 - (NSComparisonResult) compare: (id) otherFolder
273 NSComparisonResult comparison;
275 if ([NSStringFromClass([otherFolder class])
276 isEqualToString: @"SOGoContactLDAPFolder"])
277 comparison = NSOrderedAscending;
279 comparison = [super compare: otherFolder];
286 - (NSString *) folderType
291 - (NSString *) outlookFolderClass
293 return @"IPF.Contact";
296 @end /* SOGoContactGCSFolder */