2 Copyright (C) 2002-2004 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 #include "SoObject+SoDAV.h"
24 #include "SoObjectDataSource.h"
25 #include "EOFetchSpecification+SoDAV.h"
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WOContext.h>
28 #include <NGObjWeb/WEClientCapabilities.h>
29 #include <EOControl/EOQualifier.h>
30 #include <EOControl/EOSortOrdering.h>
33 @implementation NSObject(SoObjectDAVQueries)
35 static int debugBulk = NO; // TODO: set to -1 and use defaults
37 - (NSEnumerator *)davChildKeysInContext:(id)_ctx {
39 This returns the names of the children of a collection.
40 Could return toOneRelationshipKeys+toManyRelationshipKeys ?
42 NSClassDescription *cd;
46 Note: this is done explicitly because the WebDAV class description
47 can be different to the 'EOF' class description.
49 if ((cd = [self soClassDescription]) != nil) {
50 t1 = [cd toOneRelationshipKeys];
51 tn = [cd toManyRelationshipKeys];
54 t1 = [self toOneRelationshipKeys];
55 tn = [self toManyRelationshipKeys];
59 return [t1 objectEnumerator];
61 return [tn objectEnumerator];
63 return [[t1 arrayByAddingObjectsFromArray:tn] objectEnumerator];
66 - (EODataSource *)contentDataSourceInContext:(id)_ctx {
67 return [[[SoObjectDataSource alloc] initWithObject:self
68 inContext:_ctx] autorelease];
71 - (EODataSource *)davFlatDataSourceInContext:(id)_ctx {
73 @"%s: this method is deprecated,use -contentDataSourceInContext: !"];
74 return [self contentDataSourceInContext:_ctx];
77 - (NSArray *)davQueryOnSelf:(EOFetchSpecification *)_fs inContext:(id)_ctx {
78 id<EOQualifierEvaluation> q;
82 if ((q = (void *)[_fs qualifier])) {
83 if (![q evaluateWithObject:self]) {
84 [self debugWithFormat:@" self does not match qualifier."];
85 return [NSArray array];
89 if ((keys = [_fs selectedWebDAVPropertyNames]) == nil) {
91 Note: this should not happen anymore, a default-set will be used by
94 keys = [[self soClassDescription] attributeKeys];
95 [self debugWithFormat:@"using keys from description: %@", keys];
98 /* ensure that the URL is added */
99 keys = [keys arrayByAddingObject:@"davURL"];
101 if ([_fs queryWebDAVPropertyNamesOnly]) {
102 /* how does the renderer know, that these are the keys ? */
103 [self debugWithFormat:@"deliver keys only: %@", keys];
104 return keys != nil ? [NSArray arrayWithObject:keys] : nil;
107 // TODO: we should map out certain keys, like 'retain', 'release' etc!
108 if ((values = [self valuesForKeys:keys]) == nil)
111 return [NSArray arrayWithObject:values];
114 - (id)performWebDAVDeepQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
115 /* this just does a flat search :-(, maybe we should return 403 ? */
116 WEClientCapabilities *cc;
120 BOOL includeSelf, doDeep;
123 /* setup scope info */
125 scope = [_fs scopeOfWebDAVQuery];
126 includeSelf = [scope rangeOfString:@"-self"].length == 0 ? NO : YES;
129 /* do some user-agent specific tolerance */
131 cc = [[(id <WOPageGenerationContext>)_ctx request] clientCapabilities];
135 ua = [cc userAgentType];
136 if ([ua isEqualToString:@"Evolution"] || [ua isEqualToString:@"WebFolder"])
139 [self logWithFormat:@"return self on UA: %@", ua];
145 /* perform deep query */
147 ds = [self contentDataSourceInContext:_ctx];
148 [ds setFetchSpecification:_fs];
150 if ((result = [ds fetchObjects]) == nil)
154 if ((count = [result count]) == 0) {
156 return [self davQueryOnSelf:_fs inContext:_ctx];
161 ma = [NSMutableArray arrayWithCapacity:(count + 2)];
164 [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
166 /* add flat results */
167 [ma addObjectsFromArray:result];
171 Should walk over each result and reperform the query with deep-self.
172 If the results came from SoObjectDataSource this is possible because
173 the "full" object is none in the SoObjectResultEntry.
176 @"WARNING: attempted deep-search, not supported yet."];
183 - (id)performWebDAVBulkQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
184 NSAutoreleasePool *pool;
185 EOFetchSpecification *subSpec;
186 NSMutableArray *results;
190 pool = [[NSAutoreleasePool alloc] init];
191 //[self debugWithFormat:@"perform bulk query ..."];
193 /* create a sub-fetch-spec */
195 NSMutableDictionary *hints;
197 hints = [[_fs hints] mutableCopy];
198 [hints removeObjectForKey:@"bulkTargetKeys"];
200 subSpec = [[_fs copy] autorelease];
201 [subSpec setHints:hints];
205 results = [NSMutableArray arrayWithCapacity:256];
206 keys = [[_fs davBulkTargetKeys] objectEnumerator];
207 while ((key = [keys nextObject])) {
212 [self debugWithFormat:@" check bulk key: '%@'", key];
216 if ([key rangeOfString:@"/"].length == 0) {
217 /* simple key, just use -lookupName */
218 child = [self lookupName:key inContext:_ctx acquire:NO];
220 [self logWithFormat:@"ERROR: did not find the BPROPFIND target '%@'",
226 /* complex key, try to traverse */
227 // TODO: pass auth parameters to traversal context !!
228 child = [self traversePath:key acquire:NO];
231 @"ERROR: did not find the BPROPFIND target '%@' by traversing.",
235 if ([child isKindOfClass:[NSException class]]) {
236 [self logWithFormat:@"traversing bulk path '%@', failed: %@",
240 [self logWithFormat:@"traversed bulk path '%@', got: %@", key, child];
243 /* set entity name */
244 [subSpec setEntityName:
245 [[_fs entityName] stringByAppendingPathComponent:key]];
247 /* perform subquery */
248 childResults = [child performWebDAVQuery:subSpec inContext:_ctx];
250 if ([childResults isKindOfClass:[NSArray class]])
251 [results addObjectsFromArray:childResults];
253 [results addObject:childResults];
257 results = [results retain];
259 return [results autorelease];
262 - (id)performWebDAVQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
264 NSArray *bulkQueryKeys;
266 if (_fs == nil) return nil;
268 if ((bulkQueryKeys = [_fs davBulkTargetKeys]))
269 return [self performWebDAVBulkQuery:_fs inContext:_ctx];
271 scope = [_fs scopeOfWebDAVQuery];
272 if ([scope hasPrefix:@"flat"]) {
276 ds = [self contentDataSourceInContext:_ctx];
277 [ds setFetchSpecification:_fs];
278 if ((result = [ds fetchObjects]) == nil)
282 if ([scope rangeOfString:@"+self"].length > 0) {
283 /* should include self */
286 // TODO: don't add self if we work on ZideLook or WebFolders !
288 if ((len = [result count]) == 0)
289 result = [self davQueryOnSelf:_fs inContext:_ctx];
293 ma = [NSMutableArray arrayWithCapacity:(len + 2)];
294 [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
295 [ma addObjectsFromArray:result];
302 if ([scope hasPrefix:@"self"])
303 return [self davQueryOnSelf:_fs inContext:_ctx];
305 if ([scope hasPrefix:@"deep"])
306 return [self performWebDAVDeepQuery:_fs inContext:_ctx];
308 [self logWithFormat:@"ERROR: called with invalid scope '%@'", scope];
312 @end /* NSObject(SoObjectDAVQueries) */