/* Copyright (C) 2002-2004 SKYRIX Software AG This file is part of OpenGroupware.org. OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. OGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with OGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // $Id$ #include "SoPageInvocation.h" #include "SoClassSecurityInfo.h" #include "SoProduct.h" #include "WOContext+SoObjects.h" #include #include #include #include #include #include #include #include #include "common.h" @interface WOComponent(UsedPrivates) /* this is defined in WOPageRequestHandler */ - (id)performActionNamed:(NSString *)_actionName; - (id)defaultAction; - (void)setResourceManager:(WOResourceManager *)_rm; @end @interface WOContext(UsedPrivates) - (void)setPage:(WOComponent *)_page; @end @implementation SoPageInvocation static int debugOn = 0; + (void)initialize { static BOOL didInit = NO; if (!didInit) { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; didInit = YES; debugOn = [ud boolForKey:@"SoPageInvocationDebugEnabled"] ? 1 : 0; } } - (id)initWithPageName:(NSString *)_pageName actionName:(NSString *)_action { if ((self = [super init])) { self->pageName = [_pageName copy]; self->actionName = [_action copy]; } return self; } - (id)initWithPageName:(NSString *)_pageName { return [self initWithPageName:_pageName actionName:nil]; } - (id)init { return [self initWithPageName:nil]; } - (id)initWithPageName:(NSString *)_pageName actionName:(NSString *)_action product:(SoProduct *)_product { if ((self = [self initWithPageName:_pageName actionName:_action])) { self->product = _product; } return self; } - (void)dealloc { [self->argumentSpecifications release]; [self->page release]; [self->object release]; [self->pageName release]; [self->actionName release]; [super dealloc]; } /* containment */ - (void)detachFromContainer { self->product = nil; } - (id)container { return self->product; } - (NSString *)nameInContainer { /* could ask product */ return nil; } /* accessors */ - (NSString *)pageName { return self->pageName; } - (NSString *)actionName { return self->actionName; } - (void)setArgumentSpecifications:(NSDictionary *)_specs { ASSIGNCOPY(self->argumentSpecifications, _specs); } - (NSDictionary *)argumentSpecifications { return self->argumentSpecifications; } /* argument processing */ - (NSDictionary *)extractSOAPArgumentsFromContext:(id)_ctx specification:(id)_spec { /* spec is supposed to be a dictionary with the KVC keys as the keys and DOM query pathes as the values. */ NSMutableDictionary *args; NSEnumerator *keys; NSString *key; id soapEnvelope; // TODO: I guess that should be improved a bit in the dispatcher if ((soapEnvelope = [_ctx valueForKey:@"SOAPEnvelope"]) == nil) { // TODO: generate some kind of fault? (NSException?) [self logWithFormat:@"ERROR: no SOAP envelope available in context!"]; return nil; } /* for each positional selector argument we have a query path */ args = [NSMutableDictionary dictionaryWithCapacity:8]; keys = [_spec keyEnumerator]; while ((key = [keys nextObject])) { NSString *qppath; id value; qppath = [_spec valueForKey:key]; value = [qppath isNotNull] ? [soapEnvelope lookupQueryPath:qppath] : nil; [args setObject:(value != nil ? value : [NSNull null]) forKey:key]; } return args; } - (NSDictionary *)extractArgumentsFromContext:(id)_ctx forRequestType:(NSString *)_type specification:(id)_spec { if ([_type isEqualToString:@"SOAP"]) return [self extractSOAPArgumentsFromContext:_ctx specification:_spec]; [self logWithFormat: @"ERROR: cannot extract parameters for request type: '%@'", _type]; return nil; } /* page construction */ - (WOComponent *)instantiatePageInContext:(id)_ctx { WOResourceManager *rm; WOComponent *lPage; NSArray *languages; if (debugOn) { [self debugWithFormat:@"instantiate page: %@", self->pageName]; if (self->product == nil) [self debugWithFormat:@" no product is set."]; } if (_ctx == nil) { [self debugWithFormat: @"Note: got no explicit context for page instantiation, using " @"application context."]; _ctx = [[WOApplication application] context]; } /* lookup available resource manager (product,component,app) */ if ((rm = [self->product resourceManager]) == nil) { if ((rm = [[_ctx component] resourceManager]) == nil) { rm = [[_ctx application] resourceManager]; if (debugOn) [self debugWithFormat:@" app-rm: %@", rm]; } else if (debugOn) [self debugWithFormat:@" component-rm: %@", rm]; } else if (debugOn) [self debugWithFormat:@" product-rm: %@", rm]; /* determine language */ languages = [_ctx hasSession] ? [(WOSession *)[_ctx session] languages] : [[(id )_ctx request] browserLanguages]; /* instantiate */ lPage = [rm pageWithName:self->pageName languages:languages]; [lPage ensureAwakeInContext:_ctx]; [lPage setResourceManager:rm]; if (debugOn) [self debugWithFormat:@" page: %@", lPage]; return lPage; } /* invocation */ - (BOOL)isCallable { return YES; } - (id)clientObject { return self->object; } - (id)callOnObject:(id)_client inContext:(id)_ctx { NSDictionary *argspec; WOComponent *lPage; WORequest *rq; if (self->object != _client) { /* rebind */ return [[self bindToObject:_client inContext:_ctx] callOnObject:_client inContext:_ctx]; } if ((lPage = self->page) == nil) lPage = [self instantiatePageInContext:_ctx]; if (lPage == nil) { [self logWithFormat:@"found no page named '%@' for call !", self->pageName]; return nil; } /* make page the "request" page */ [_ctx setPage:lPage]; /* set client object in page */ [lPage setClientObject:_client]; /* apply request parameters */ rq = [(id )_ctx request]; if ([lPage shouldTakeValuesFromRequest:rq inContext:_ctx]) { [[_ctx application] takeValuesFromRequest:rq inContext:_ctx]; } /* apply extracted parameters (TODO: what should be done first?) */ argspec = [self->argumentSpecifications objectForKey:[_ctx soRequestType]]; if (argspec != nil) { NSDictionary *args; args = [self extractArgumentsFromContext:_ctx forRequestType:[_ctx soRequestType] specification:argspec]; if (debugOn) [self debugWithFormat:@"extracted args %@", args]; if (args != nil) [lPage takeValuesFromDictionary:args]; } /* call action */ if (self->actionName) { if (debugOn) { [self debugWithFormat:@" performing action %@ on page: %@", self->actionName, lPage]; } return [lPage performActionNamed:self->actionName]; } else { if (debugOn) { [self debugWithFormat:@" performing default action on page: %@", lPage]; } return [lPage defaultAction]; } } /* bindings */ - (BOOL)isBound { return self->object != nil ? YES : NO; } - (id)bindToObject:(id)_object inContext:(id)_ctx { SoPageInvocation *inv; if (_object == nil) return nil; // TODO: clean up this section, a bit hackish inv = [[SoPageInvocation alloc] initWithPageName:self->pageName]; inv = [inv autorelease]; inv->product = self->product; // non-owned (cannot be detached !!!) inv->object = [_object retain]; inv->actionName = [self->actionName copy]; inv->page = [[inv instantiatePageInContext:_ctx] retain]; inv->argumentSpecifications = [self->argumentSpecifications copy]; if (inv->page == nil) { [self logWithFormat:@"ERROR: did not find page method '%@'", self->pageName]; return nil; } return inv; } /* delivering as content (can happen in DAV !) */ - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx { [_r appendContentString:@"native component method: "]; [_r appendContentHTMLString:[self description]]; } /* key/value coding */ - (id)valueForUndefinedKey:(NSString *)_key { if (debugOn) [self debugWithFormat:@"return nil for KVC key: '%@'", _key]; return nil; } /* description */ - (NSString *)description { NSMutableString *ms; ms = [NSMutableString stringWithCapacity:64]; [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])]; if (self->pageName) [ms appendFormat:@" page=%@", self->pageName]; if (self->actionName) [ms appendFormat:@" action=%@", self->actionName]; if (self->object) [ms appendString:@" bound"]; if (self->page) [ms appendString:@" instantiated"]; if (self->product) [ms appendFormat:@" product=%@", self->product]; if ([self->argumentSpecifications count] > 0) { id tmp; tmp = [self->argumentSpecifications allKeys]; tmp = [tmp componentsJoinedByString:@","]; [ms appendFormat:@" arg-handlers=%@",tmp]; } [ms appendString:@">"]; return ms; } @end /* SoPageInvocation */ @implementation SoPageInvocation(Logging) - (NSString *)loggingPrefix { return [NSString stringWithFormat:@"[so-page 0x%08X %@]", self, self->pageName]; } - (BOOL)isDebuggingEnabled { return debugOn ? YES : NO; } @end /* SoPageInvocation(Logging) */