]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoPageInvocation.m
fixed OGo bug #888
[sope] / sope-appserver / NGObjWeb / SoObjects / SoPageInvocation.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 // $Id$
22
23 #include "SoPageInvocation.h"
24 #include "SoClassSecurityInfo.h"
25 #include "SoProduct.h"
26 #include "WOContext+SoObjects.h"
27 #include <NGObjWeb/WOComponent.h>
28 #include <NGObjWeb/WOContext.h>
29 #include <NGObjWeb/WOSession.h>
30 #include <NGObjWeb/WOResponse.h>
31 #include <NGObjWeb/WORequest.h>
32 #include <NGObjWeb/WOResourceManager.h>
33 #include <NGObjWeb/WOApplication.h>
34 #include <DOM/EDOM.h>
35 #include "common.h"
36
37 @interface WOComponent(UsedPrivates)
38 /* this is defined in WOPageRequestHandler */
39 - (id<WOActionResults>)performActionNamed:(NSString *)_actionName;
40 - (id<WOActionResults>)defaultAction;
41 - (void)setResourceManager:(WOResourceManager *)_rm;
42 @end
43
44 @interface WOContext(UsedPrivates)
45 - (void)setPage:(WOComponent *)_page;
46 @end
47
48 @implementation SoPageInvocation
49
50 static int debugOn = 0;
51
52 + (void)initialize {
53   static BOOL didInit = NO;
54   if (!didInit) {
55     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
56     didInit = YES;
57     
58     debugOn = [ud boolForKey:@"SoPageInvocationDebugEnabled"] ? 1 : 0;
59   }
60 }
61
62 - (id)initWithPageName:(NSString *)_pageName actionName:(NSString *)_action {
63   if ((self = [super init])) {
64     self->pageName   = [_pageName copy];
65     self->actionName = [_action   copy];
66   }
67   return self;
68 }
69 - (id)initWithPageName:(NSString *)_pageName {
70   return [self initWithPageName:_pageName actionName:nil];
71 }
72 - (id)init {
73   return [self initWithPageName:nil];
74 }
75 - (id)initWithPageName:(NSString *)_pageName actionName:(NSString *)_action
76   product:(SoProduct *)_product
77 {
78   if ((self = [self initWithPageName:_pageName actionName:_action])) {
79     self->product = _product;
80   }
81   return self;
82 }
83
84 - (void)dealloc {
85   [self->argumentSpecifications release];
86   [self->page       release];
87   [self->object     release];
88   [self->pageName   release];
89   [self->actionName release];
90   [super dealloc];
91 }
92
93 /* containment */
94
95 - (void)detachFromContainer {
96   self->product = nil;
97 }
98 - (id)container {
99   return self->product;
100 }
101 - (NSString *)nameInContainer {
102   /* could ask product */
103   return nil;
104 }
105
106 /* accessors */
107
108 - (NSString *)pageName {
109   return self->pageName;
110 }
111 - (NSString *)actionName {
112   return self->actionName;
113 }
114
115 - (void)setArgumentSpecifications:(NSDictionary *)_specs {
116   ASSIGNCOPY(self->argumentSpecifications, _specs);
117 }
118 - (NSDictionary *)argumentSpecifications {
119   return self->argumentSpecifications;
120 }
121
122 /* argument processing */
123
124 - (NSDictionary *)extractSOAPArgumentsFromContext:(id)_ctx 
125   specification:(id)_spec 
126 {
127   /* 
128      spec is supposed to be a dictionary with the KVC keys as the 
129      keys and DOM query pathes as the values.
130   */
131   NSMutableDictionary *args;
132   NSEnumerator *keys;
133   NSString     *key;
134   id           soapEnvelope;
135   
136   // TODO: I guess that should be improved a bit in the dispatcher
137   if ((soapEnvelope = [_ctx valueForKey:@"SOAPEnvelope"]) == nil) {
138     // TODO: generate some kind of fault? (NSException?)
139     [self logWithFormat:@"ERROR: no SOAP envelope available in context!"];
140     return nil;
141   }
142   
143   /* for each positional selector argument we have a query path */
144   
145   args = [NSMutableDictionary dictionaryWithCapacity:8];
146   keys = [_spec keyEnumerator];
147   while ((key = [keys nextObject])) {
148     NSString *qppath;
149     id value;
150     
151     qppath = [_spec valueForKey:key];
152     value  = [qppath isNotNull] ? [soapEnvelope lookupQueryPath:qppath] : nil;
153     
154     [args setObject:(value != nil ? value : [NSNull null]) forKey:key];
155   }
156   return args;
157 }
158
159 - (NSDictionary *)extractArgumentsFromContext:(id)_ctx
160   forRequestType:(NSString *)_type
161   specification:(id)_spec 
162 {
163   if ([_type isEqualToString:@"SOAP"])
164     return [self extractSOAPArgumentsFromContext:_ctx specification:_spec];
165   
166   [self logWithFormat:
167           @"ERROR: cannot extract parameters for request type: '%@'", _type];
168   return nil;
169 }
170
171 /* page construction */
172
173 - (WOComponent *)instantiatePageInContext:(id)_ctx {
174   WOResourceManager *rm;
175   WOComponent *lPage;
176   NSArray     *languages;
177   
178   if (debugOn) {
179     [self debugWithFormat:@"instantiate page: %@", self->pageName];
180     if (self->product == nil)
181       [self debugWithFormat:@"  no product is set."];
182   }
183
184   if (_ctx == nil) {
185     [self debugWithFormat:
186             @"Note: got no explicit context for page instantiation, using "
187             @"application context."];
188     _ctx = [[WOApplication application] context];
189   }
190   
191   /* lookup available resource manager (product,component,app) */
192   
193   if ((rm = [self->product resourceManager]) == nil) {
194     if ((rm = [[_ctx component] resourceManager]) == nil) {
195       rm = [[_ctx application] resourceManager];
196       if (debugOn) [self debugWithFormat:@"   app-rm: %@", rm];
197     }
198     else
199       if (debugOn) [self debugWithFormat:@"   component-rm: %@", rm];
200   }
201   else
202     if (debugOn) [self debugWithFormat:@"   product-rm: %@", rm];
203   
204   /* determine language */
205   
206   languages = [_ctx hasSession]
207     ? [(WOSession *)[_ctx session] languages]
208     : [[(id <WOPageGenerationContext>)_ctx request] browserLanguages];
209
210   /* instantiate */
211   
212   lPage = [rm pageWithName:self->pageName languages:languages];
213   [lPage ensureAwakeInContext:_ctx];
214   [lPage setResourceManager:rm];
215   
216   if (debugOn) [self debugWithFormat:@"   page: %@", lPage];
217   
218   return lPage;
219 }
220
221 /* invocation */
222
223 - (BOOL)isCallable {
224   return YES;
225 }
226 - (id)clientObject {
227   return self->object;
228 }
229
230 - (id)callOnObject:(id)_client inContext:(id)_ctx {
231   NSDictionary *argspec;
232   WOComponent  *lPage;
233   WORequest    *rq;
234   
235   if (self->object != _client) {
236     /* rebind */
237     return [[self bindToObject:_client inContext:_ctx]
238                   callOnObject:_client inContext:_ctx];
239   }
240   
241   if ((lPage = self->page) == nil)
242     lPage = [self instantiatePageInContext:_ctx];
243   
244   if (lPage == nil) {
245     [self logWithFormat:@"found no page named '%@' for call !", 
246           self->pageName];
247     return nil;
248   }
249   
250   /* make page the "request" page */
251   
252   [_ctx setPage:lPage];
253
254   /* set client object in page */
255   
256   [lPage setClientObject:_client];
257   
258   /* apply request parameters */
259   
260   rq = [(id <WOPageGenerationContext>)_ctx request];
261   
262   if ([lPage shouldTakeValuesFromRequest:rq inContext:_ctx]) {
263     [[_ctx application] takeValuesFromRequest:rq
264                         inContext:_ctx];
265   }
266   
267   /* apply extracted parameters (TODO: what should be done first?) */
268
269   argspec = [self->argumentSpecifications objectForKey:[_ctx soRequestType]];
270   if (argspec != nil) {
271     NSDictionary *args;
272     
273     args = [self extractArgumentsFromContext:_ctx
274                  forRequestType:[_ctx soRequestType]
275                  specification:argspec];
276     if (debugOn) [self debugWithFormat:@"extracted args %@", args];
277     
278     if (args != nil) [lPage takeValuesFromDictionary:args];
279   }
280   
281   /* call action */
282   
283   if (self->actionName) {
284     if (debugOn) {
285       [self debugWithFormat:@"  performing action %@ on page: %@", 
286               self->actionName, lPage];
287     }
288     return [lPage performActionNamed:self->actionName];
289   }
290   else {
291     if (debugOn) {
292       [self debugWithFormat:@"  performing default action on page: %@", 
293               lPage];
294     }
295     return [lPage defaultAction];
296   }
297 }
298
299 /* bindings */
300
301 - (BOOL)isBound {
302   return self->object != nil ? YES : NO;
303 }
304
305 - (id)bindToObject:(id)_object inContext:(id)_ctx {
306   SoPageInvocation *inv;
307   
308   if (_object == nil) return nil;
309   
310   // TODO: clean up this section, a bit hackish
311   inv = [[SoPageInvocation alloc] initWithPageName:self->pageName];
312   inv = [inv autorelease];
313   
314   inv->product    = self->product; // non-owned (cannot be detached !!!)
315   inv->object     = [_object retain];
316   inv->actionName = [self->actionName copy];
317   inv->page       = [[inv instantiatePageInContext:_ctx] retain];
318   inv->argumentSpecifications = [self->argumentSpecifications copy];
319   
320   if (inv->page == nil) {
321     [self logWithFormat:@"ERROR: did not find page method '%@'", 
322             self->pageName];
323     return nil;
324   }
325   
326   return inv;
327 }
328
329 /* delivering as content (can happen in DAV !) */
330
331 - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
332   [_r appendContentString:@"native component method: "];
333   [_r appendContentHTMLString:[self description]];
334 }
335
336 /* key/value coding */
337
338 - (id)valueForUndefinedKey:(NSString *)_key {
339   if (debugOn) [self debugWithFormat:@"return nil for KVC key: '%@'", _key];
340   return nil;
341 }
342
343 /* description */
344
345 - (NSString *)description {
346   NSMutableString *ms;
347
348   ms = [NSMutableString stringWithCapacity:64];
349   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
350   
351   if (self->pageName)   [ms appendFormat:@" page=%@",   self->pageName];
352   if (self->actionName) [ms appendFormat:@" action=%@", self->actionName];
353   
354   if (self->object) [ms appendString:@" bound"];
355   if (self->page)   [ms appendString:@" instantiated"];
356   
357   if (self->product) [ms appendFormat:@" product=%@", self->product];
358   
359   if ([self->argumentSpecifications count] > 0) {
360     id tmp;
361     
362     tmp = [self->argumentSpecifications allKeys];
363     tmp = [tmp componentsJoinedByString:@","];
364     [ms appendFormat:@" arg-handlers=%@",tmp];
365   }
366   
367   [ms appendString:@">"];
368   return ms;
369 }
370
371 @end /* SoPageInvocation */
372
373 @implementation SoPageInvocation(Logging)
374
375 - (NSString *)loggingPrefix {
376   return [NSString stringWithFormat:@"[so-page 0x%08X %@]", 
377                      self, self->pageName];
378 }
379 - (BOOL)isDebuggingEnabled {
380   return debugOn ? YES : NO;
381 }
382
383 @end /* SoPageInvocation(Logging) */