1/*
2  Copyright (C) 2000-2005 SKYRIX Software AG
3
4  This file is part of SOPE.
5
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
9  later version.
10
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.
15
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
19  02111-1307, USA.
20*/
21
22#include "WODirectActionRequestHandler.h"
23#include "WORequestHandler+private.h"
24#include "WOContext+private.h"
25#include <NGObjWeb/WOApplication.h>
26#include <NGObjWeb/WOComponent.h>
27#include <NGObjWeb/WODirectAction.h>
28#include <NGObjWeb/WORequest.h>
29#include <NGObjWeb/WOResponse.h>
30#include <NGObjWeb/WOSession.h>
31#include <NGObjWeb/WOSessionStore.h>
32#include <NGObjWeb/WOStatisticsStore.h>
33#include "common.h"
34
35#if APPLE_RUNTIME || NeXT_RUNTIME
36#  include <objc/objc-class.h>
37#endif
38
39static BOOL  usePool = NO;
40static BOOL  perflog = NO;
41static BOOL  debugOn = NO;
42static Class NSDateClass = Nil;
43
44@implementation WODirectActionRequestHandler
45
46+ (void)initialize {
47  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48
49  NSDateClass = [NSDate class];
50  perflog = [ud boolForKey:@"WOProfileDirectActionRequestHandler"];
51}
52
53- (NSString *)loggingPrefix {
54  return @"[da-handler]";
55}
56
57/*
58  The request handler part of a direct action URI looks like this:
59
60    [actionClass/]actionName[?key=value&key=value&...]
61*/
62
63- (BOOL)isComponentClass:(Class)_clazz {
64  if (_clazz == Nil)
65    return NO;
66#if (defined(__GNU_LIBOBJC__) && (__GNU_LIBOBJC__ >= 20100911)) || defined(APPLE_RUNTIME) || defined(__GNUSTEP_RUNTIME__)
67  while ((_clazz = class_getSuperclass(_clazz)) != Nil) {
68#else
69  while ((_clazz = _clazz->super_class) != Nil) {
70#endif
71    if (_clazz == [WOComponent    class]) return YES;
72    if (_clazz == [WODirectAction class]) return NO;
73    if (_clazz == [NSObject       class]) return NO;
74  }
75  return NO;
76}
77
78- (id)instantiateObjectForActionClass:(Class)actionClass
79  inContext:(WOContext *)context
80  application:(WOApplication *)app
81{
82  WOComponent *component;
83
84  if (actionClass == Nil)
85    return nil;
86
87  if (![self isComponentClass:actionClass]) {
88    /* create direct action object */
89    id actionObject;
90
91    if (![actionClass instancesRespondToSelector:
92			@selector(initWithContext:)]) {
93      [self logWithFormat:@"tried to use class '%@' as a direct-action class",
94	      NSStringFromClass(actionClass)];
95      return nil;
96    }
97
98    actionObject =
99      [(WODirectAction *)[actionClass alloc] initWithContext:context];
100    actionObject = [actionObject autorelease];
101    return actionObject;
102  }
103
104  /* special initialization for WOComponents used as direct actions */
105
106  component = [app pageWithName:NSStringFromClass(actionClass)
107		   inContext:context];
108  [context setPage:(id)component];
109
110  if ([component shouldTakeValuesFromRequest:[context request]
111		 inContext:context])
112    [app takeValuesFromRequest:[context request] inContext:context];
113
114  return component;
115}
116
117- (WOResponse *)handleRequest:(WORequest *)_request
118  inContext:(WOContext *)context
119  session:(WOSession *)session
120  application:(WOApplication *)app
121{
122  NSAutoreleasePool   *pool2;
123  NSString            *actionClassName;
124  NSString            *actionName;
125  WOResponse          *response;
126  NSArray             *handlerPath;
127  Class               actionClass = Nil;
128  WODirectAction      *actionObject = nil;
129  id<WOActionResults> result = nil;
130
131  pool2 = usePool ? [[NSAutoreleasePool alloc] init] : nil;
132
133  *(&result) = nil;
134  *(&response)        = nil;
135  *(&actionClassName) = nil;
136  *(&actionName)      = nil;
137  *(&handlerPath)     = nil;
138
139  /* process path */
140
141  handlerPath = [_request requestHandlerPathArray];
142
143  if (debugOn) {
144    [self debugWithFormat:@"path=%@ array=%@",
145          [_request requestHandlerPath], handlerPath];
146  }
147
148  // TODO: fix OGo bug #1028
149  switch ([handlerPath count]) {
150    case 0:
151      actionClassName = @"DirectAction";
152      actionName      = @"default";
153      break;
154    case 1:
155      actionClassName = @"DirectAction";
156      actionName      = [handlerPath objectAtIndex:0];
157      break;
158    case 2:
159      actionClassName = [handlerPath objectAtIndex:0];
160      actionName      = [handlerPath objectAtIndex:1];
161      break;
162
163    default:
164      actionClassName = [handlerPath objectAtIndex:0];
165      actionName      = [handlerPath objectAtIndex:1];
166      // TODO: set path info in ctx?
167      if (debugOn) {
168	[self logWithFormat:@"invalid direction action URL: %@",
169                [_request requestHandlerPath]];
170      }
171      break;
172  }
173
174  if ([actionName length] == 0)
175    actionName = @"default";
176
177  if ((*(&actionClass) = NSClassFromString(actionClassName)) == Nil) {
178    [self errorWithFormat:@"did not find direct action class %@",
179            actionClassName];
180    actionClass = [WODirectAction class];
181  }
182
183  if (debugOn) {
184    [self debugWithFormat:
185	    @"[direct action request handler] class=%@ action=%@ ..",
186            actionClassName, actionName];
187  }
188
189  /* process request */
190
191  actionObject = [self instantiateObjectForActionClass:actionClass
192		       inContext:context
193		       application:app];
194
195  if (actionObject == nil) {
196    [self errorWithFormat:
197            @"could not create direct action object of class %@",
198            actionClassName];
199    actionObject = nil;
200  }
201  else {
202    static Class WOComponentClass = Nil;
203
204    if (WOComponentClass == Nil)
205      WOComponentClass = [WOComponent class];
206
207    result = [(id)[actionObject performActionNamed:actionName] retain];
208
209    if (result == nil) result = [[context page] retain];
210
211    if ([(id)result isKindOfClass:WOComponentClass]) {
212      [(id)result _awakeWithContext:context];
213      [context setPage:(WOComponent *)result];
214
215      response = [self generateResponseForComponent:(WOComponent *)result
216                       inContext:context
217                       application:app];
218
219      if ([context hasSession]) {
220        if ([context savePageRequired])
221          [[context session] savePage:(WOComponent *)result];
222      }
223
224      response = [response retain];
225    }
226    else {
227      /* generate response */
228      response = [[result generateResponse] retain];
229    }
230
231    [context sleepComponents];
232
233    [(id)result release]; result = nil;
234
235    /* check whether a session was created */
236    if ((session == nil) && [context hasSession]) {
237      session = [[[context session] retain] autorelease];
238      [session lock];
239    }
240
241    if (usePool) {
242      session = [session retain];
243      [pool2 release]; pool2 = nil;
244      session = [session autorelease];
245    }
246    response = [response autorelease];
247  }
248
249  return response;
250}
251
252@end /* WODirectActionRequestHandler */
253