]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WebDAV/SoObjectDataSource.m
fixed umlaut issue on MacOSX
[sope] / sope-appserver / NGObjWeb / WebDAV / SoObjectDataSource.m
1 /*
2   Copyright (C) 2002-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "SoObjectDataSource.h"
23 #include "SoObjectResultEntry.h"
24 #include "SoObject+SoDAV.h"
25 #include "SoObject.h"
26 #include "EOFetchSpecification+SoDAV.h"
27 #include <NGObjWeb/WEClientCapabilities.h>
28 #include <NGObjWeb/WOContext.h>
29 #include <EOControl/EOQualifier.h>
30 #include <EOControl/EOSortOrdering.h>
31 #include "common.h"
32
33 @implementation SoObjectDataSource
34
35 static BOOL debugOn = NO;
36
37 + (void)initialize {
38   NSUserDefaults *ud;
39   
40   ud = [NSUserDefaults standardUserDefaults];
41   debugOn = [ud boolForKey:@"SoObjectDataSourceDebugEnabled"];
42 }
43
44 - (id)initWithObject:(id)_object inContext:(id)_ctx {
45   if ((self = [super init])) {
46     self->object  = [_object retain];
47     self->context = _ctx;
48   }
49   return self;
50 }
51 - (id)init {
52   return [self initWithObject:nil inContext:nil];
53 }
54
55 - (void)dealloc {
56   [self->object release];
57   [super dealloc];
58 }
59
60
61 /* accessors */
62
63 - (void)setFetchSpecification:(EOFetchSpecification *)_fetchSpec {
64   if ([_fetchSpec isEqual:self->fspec]) return;
65   
66   ASSIGN(self->fspec, _fetchSpec);
67   [self postDataSourceChangedNotification];
68 }
69 - (EOFetchSpecification *)fetchSpecification {
70   return self->fspec;
71 }
72
73 - (id)context {
74   return self->context;
75 }
76 - (id)object {
77   return self->object;
78 }
79
80 - (EOClassDescription *)classDescriptionForObjects {
81   if ([self->object respondsToSelector:_cmd])
82     /* forward to datasource owner if possible */
83     return [self->object performSelector:_cmd];
84   
85   return nil;
86 }
87
88 /* implementation */
89
90 - (NSArray *)davFlatQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
91   /*
92     If you have a datasource for your children, consider to override this
93     method and use the DS to perform the query.
94   */
95   id<EOQualifierEvaluation> q;
96   NSMutableArray *ma;
97   NSArray      *result;
98   NSEnumerator *childKeys;
99   NSArray      *queriedAttrNames;
100   NSArray      *orderings;
101   NSString     *childKey;
102   BOOL         doEscape;
103   NSString     *entityURL;
104   BOOL         isBrief = YES; // do not encode "sub-errors", just omit the item
105   
106   [self debugWithFormat:@"performing flat query: %@", _fs];
107
108   /* base URL */
109
110   entityURL = [_fs entityName];
111   if (![entityURL hasSuffix:@"/"]) 
112     entityURL = [entityURL stringByAppendingString:@"/"];
113   
114   doEscape = YES;
115   
116   /* retrieve child names (calls -toOneRelationshipKeys and -toManyRel...) */
117   
118   childKeys = [self->object davChildKeysInContext:_ctx];
119   
120   /* process */
121   
122   [self debugWithFormat:@"query keys: %@", childKeys];
123     
124   q  = (void *)[_fs qualifier];
125   ma = [NSMutableArray arrayWithCapacity:16];
126     
127   queriedAttrNames = [_fs selectedWebDAVPropertyNames];
128   
129   while ((childKey = [[childKeys nextObject] stringValue]) != nil) {
130     NSDictionary *rec;
131     NSException  *e;
132     NSString     *childHref;
133     id child = nil;
134       
135     /* 
136        TODO: question of the week, is it faster to filter on the record
137        dictionary or to filter on the object itself? Do WebDAV clients
138        always select all items they sort or filter? Hm.
139     */
140     rec = nil;
141     
142     if ((e = [self->object validateName:childKey inContext:_ctx])) {
143       /* security problem */
144       [self debugWithFormat:@"  child key '%@' did not validate:\n  %@", 
145               childKey, e];
146         
147       if (isBrief) // when the brief header is set, forget errors
148         e = nil;
149     }
150     else if ((child = [self->object lookupName:childKey inContext:_ctx 
151                            acquire:NO])==nil) {
152       /* not found */
153       [self debugWithFormat:@"  did not find key '%@'", childKey];
154         
155       if (!isBrief) { // when the brief header is not set, encode status
156         NSDictionary *ui;
157           
158         ui = [NSDictionary dictionaryWithObject:@"404" /* not found */
159                            forKey:@"http-status"];
160         e = [NSException exceptionWithName:@"KeyError"
161                          reason:@"failed to lookup a WebDAV child resource"
162                          userInfo:ui];
163       }
164     }
165     else if ([child isKindOfClass:[NSException class]]) {
166       e = child;
167       child = nil;
168     }
169     else {
170       /* found a valid child */
171       //[self debugWithFormat:@"  found child for key '%@'", childKey];
172       
173       if (q != nil) {
174         if (![q evaluateWithObject:child]) {
175           /* object does not match the filter */
176           if (debugOn) {
177             [self debugWithFormat:@"  object did not match qualifier: %@", 
178                     child];
179           }
180           continue;
181         }
182       }
183       
184       /* passed */
185       // TODO: child can be SoHTTPException ?
186       rec = (queriedAttrNames == nil)
187         ? child
188         : [child valuesForKeys:queriedAttrNames];
189 #if 0
190       [self logWithFormat:@"got values: %@ for keys: %@ from object: %@",
191             rec, queriedAttrNames, child];
192 #endif
193     }
194       
195     /* calc URI */
196     
197     /* 
198        Note: we cannot use NSPathUtilities, those will reformat the string on
199              Cocoa Foundation! (eg http://a => http:/a, remove double slashes)
200     */
201     childHref = doEscape ? [childKey stringByEscapingURL] : childKey;
202     childHref = [entityURL stringByAppendingString:childHref];
203     
204     if (debugOn) {
205       // TODO: this happens if we access using Goliath
206       if ([childHref hasPrefix:@"http:/"] && 
207           ![childHref hasPrefix:@"http://"]) {
208         [self logWithFormat:@"BROKEN CHILD URL: %@ (entity=%@,key=%@)", 
209               childHref, [_fs entityName], childKey];
210         abort();
211       }
212     }
213     
214     /* add errors if required */
215     
216     if ((rec == nil) && (e != nil) && !isBrief) {
217       rec = [[SoObjectErrorEntry alloc] 
218               initWithURI:childHref object:child error:e];
219     }
220     else if (rec == nil) {
221       /* either brief or no error found */
222       continue;
223     }
224     else if (queriedAttrNames) {
225       rec = [[SoObjectResultEntry alloc] 
226               initWithURI:childHref object:child values:rec];
227     }
228     else
229       /* the child */
230       rec = [rec retain];
231     
232     /* add record to result */
233     [ma addObject:rec];
234     [rec release];
235   }
236   
237   /* sort result (note: you must select anything you want to sort ...) */
238   if ((orderings = [_fs sortOrderings]))
239     [ma sortUsingKeyOrderArray:orderings];
240   
241   result = ma;
242   
243   [self debugWithFormat:@"  got %i results\n%@", [result count], result];
244   return result;
245 }
246
247 /* operations */
248
249 - (NSArray *)fetchObjects {
250   NSAutoreleasePool *pool;
251   id result;
252   
253   pool = [[NSAutoreleasePool alloc] init];
254   {
255     NSArray *keys;
256     
257     if ((keys = [[self fetchSpecification] davBulkTargetKeys])) {
258       [self logWithFormat:@"SoObjectDataSource cannot handle bulk queries !"];
259       return nil;
260     }
261     else {
262       result = [self davFlatQuery:[self fetchSpecification]
263                      inContext:[self context]];
264     }
265     result = [result retain];
266   }
267   [pool release];
268   return [result autorelease];;
269 }
270
271 /* logging */
272
273 - (NSString *)loggingPrefix {
274   return @"[object-datasource]";
275 }
276 - (BOOL)isDebuggingEnabled {
277   return debugOn;
278 }
279
280 @end /* SoObjectDataSource */