2 Copyright (C) 2002-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; 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 #import <EOControl/EOQualifier.h>
30 #import <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.
175 [self warnWithFormat:@"attempted deep-search, not supported yet."];
182 - (id)performWebDAVBulkQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
183 NSAutoreleasePool *pool;
184 EOFetchSpecification *subSpec;
185 NSMutableArray *results;
189 pool = [[NSAutoreleasePool alloc] init];
190 //[self debugWithFormat:@"perform bulk query ..."];
192 /* create a sub-fetch-spec */
194 NSMutableDictionary *hints;
196 hints = [[_fs hints] mutableCopy];
197 [hints removeObjectForKey:@"bulkTargetKeys"];
199 subSpec = [[_fs copy] autorelease];
200 [subSpec setHints:hints];
204 results = [NSMutableArray arrayWithCapacity:256];
205 keys = [[_fs davBulkTargetKeys] objectEnumerator];
206 while ((key = [keys nextObject])) {
211 [self debugWithFormat:@" check bulk key: '%@'", key];
215 if ([key rangeOfString:@"/"].length == 0) {
216 /* simple key, just use -lookupName */
217 child = [self lookupName:key inContext:_ctx acquire:NO];
219 [self errorWithFormat:@"did not find the BPROPFIND target '%@'", key];
224 /* complex key, try to traverse */
225 // TODO: pass auth parameters to traversal context !!
226 child = [self traversePath:key acquire:NO];
228 [self errorWithFormat:
229 @"did not find the BPROPFIND target '%@' by traversing.",
233 if ([child isKindOfClass:[NSException class]]) {
234 [self logWithFormat:@"traversing bulk path '%@', failed: %@",
238 [self logWithFormat:@"traversed bulk path '%@', got: %@", key, child];
241 /* set entity name */
242 [subSpec setEntityName:
243 [[_fs entityName] stringByAppendingPathComponent:key]];
245 /* perform subquery */
246 childResults = [child performWebDAVQuery:subSpec inContext:_ctx];
248 if ([childResults isKindOfClass:[NSArray class]])
249 [results addObjectsFromArray:childResults];
251 [results addObject:childResults];
255 results = [results retain];
257 return [results autorelease];
260 - (id)performWebDAVQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
262 NSArray *bulkQueryKeys;
264 if (_fs == nil) return nil;
266 if ((bulkQueryKeys = [_fs davBulkTargetKeys]))
267 return [self performWebDAVBulkQuery:_fs inContext:_ctx];
269 scope = [_fs scopeOfWebDAVQuery];
270 if ([scope hasPrefix:@"flat"]) {
274 ds = [self contentDataSourceInContext:_ctx];
275 [ds setFetchSpecification:_fs];
276 if ((result = [ds fetchObjects]) == nil)
280 if ([scope rangeOfString:@"+self"].length > 0) {
281 /* should include self */
284 // TODO: don't add self if we work on ZideLook or WebFolders !
286 if ((len = [result count]) == 0)
287 result = [self davQueryOnSelf:_fs inContext:_ctx];
291 ma = [NSMutableArray arrayWithCapacity:(len + 2)];
292 [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
293 [ma addObjectsFromArray:result];
300 if ([scope hasPrefix:@"self"])
301 return [self davQueryOnSelf:_fs inContext:_ctx];
303 if ([scope hasPrefix:@"deep"])
304 return [self performWebDAVDeepQuery:_fs inContext:_ctx];
306 [self errorWithFormat:@"called with invalid scope '%@'", scope];
310 @end /* NSObject(SoObjectDAVQueries) */