]> err.no Git - sope/blob - sope-appserver/NGObjWeb/DynamicElements/WOForm.m
hotfix
[sope] / sope-appserver / NGObjWeb / DynamicElements / WOForm.m
1 /*
2   Copyright (C) 2000-2006 SKYRIX Software AG
3   Copyright (C) 2006      Helge Hess
4
5   This file is part of SOPE.
6
7   SOPE is free software; you can redistribute it and/or modify it under
8   the terms of the GNU Lesser General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10   later version.
11
12   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15   License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with SOPE; see the file COPYING.  If not, write to the
19   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.
21 */
22
23 #include "WOForm.h"
24 #include "WOElement+private.h"
25 #include "WOInput.h"
26 #include "WOContext+private.h"
27 #include <NGObjWeb/WOApplication.h>
28 #include "decommon.h"
29
30 @implementation WOForm
31
32 static int debugTakeValues = -1;
33
34 + (int)version {
35   return 5;
36 }
37
38 - (id)initWithName:(NSString *)_name
39   associations:(NSDictionary *)_config
40   template:(WOElement *)_c
41 {
42   if (debugTakeValues == -1) {
43     debugTakeValues = 
44       [[NSUserDefaults standardUserDefaults] boolForKey:@"WODebugTakeValues"]
45       ? 1 : 0;
46     if (debugTakeValues) NSLog(@"WOForm: WODebugTakeValues on.");
47   }
48   
49   if ((self = [super initWithName:_name associations:_config template:_c])) {
50     WOAssociation *sidInUrlAssoc;
51     id tmp;
52
53     self->containsForm = YES;
54     
55     sidInUrlAssoc            = OWGetProperty(_config, @"?wosid");
56     self->action             = OWGetProperty(_config, @"action");
57     self->href               = OWGetProperty(_config, @"href");
58     self->pageName           = OWGetProperty(_config, @"pageName");
59     self->queryDictionary    = OWGetProperty(_config, @"queryDictionary");
60     self->queryParameters    = OWExtractQueryParameters(_config);
61     self->actionClass        = OWGetProperty(_config, @"actionClass");
62     self->directActionName   = OWGetProperty(_config, @"directActionName");
63     self->method             = OWGetProperty(_config, @"method");
64     self->fragmentIdentifier = OWGetProperty(_config, @"fragmentIdentifier");
65     
66     self->sidInUrl = (sidInUrlAssoc != nil)
67       ? [sidInUrlAssoc boolValueInComponent:nil]
68       : YES;
69     
70     if ((tmp = OWGetProperty(_config, @"multipleSubmit")) != nil) {
71       /* not required with SOPE, for WO compatibility */
72       [tmp release];
73     }
74     
75     self->template = [_c retain];
76   }
77   return self;
78 }
79
80 - (void)dealloc {
81   [self->fragmentIdentifier release];
82   [self->method             release];
83   [self->template           release];
84   [self->actionClass        release];
85   [self->directActionName   release];
86   [self->queryDictionary    release];
87   [self->queryParameters    release];
88   [self->action             release];
89   [self->pageName           release];
90   [self->href               release];
91   [super dealloc];  
92 }
93
94 /* handle active form elements */
95
96 - (id)template {
97   return self->template;
98 }
99
100 /* handling requests */
101
102 - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
103   static int alwaysPassIn = -1;
104
105   if (alwaysPassIn == -1) {
106     alwaysPassIn = [[[NSUserDefaults standardUserDefaults]
107                                      objectForKey:@"WOFormAlwaysPassDown"]
108                                      boolValue] ? 1 : 0;
109   }
110   
111   if ([_ctx isInForm]) {
112     [self errorWithFormat:@"(%s): another form is already active in context !",
113             __PRETTY_FUNCTION__];
114   }
115   
116   [_ctx setInForm:YES];
117   {
118     WOComponent *sComponent = [_ctx component];
119     BOOL doTakeValues = NO;
120     
121     if (self->queryParameters != nil) {
122       /* apply values to ?style parameters */
123       NSEnumerator *keys;
124       NSString     *key;
125
126       keys = [self->queryParameters keyEnumerator];
127       while ((key = [keys nextObject])) {
128         WOAssociation *assoc;
129         id value;
130         
131         assoc = [self->queryParameters objectForKey:key];
132         value = [_rq formValueForKey:key];
133
134         [assoc setValue:value inComponent:sComponent];
135       }
136     }
137     
138     // TODO: explain this href comparison
139     if ([[self->href stringValueInComponent:sComponent] 
140           isEqualToString:[_rq uri]]) {
141       if (debugTakeValues) {
142         NSArray *formValues = [_rq formValueKeys];
143         NSLog(@"%s: we are uri active (uri=%@): %@ ..", __PRETTY_FUNCTION__,
144               [_rq uri], formValues);
145       }
146       doTakeValues = YES;
147     }
148     else if ([[_ctx elementID] isEqualToString:[_ctx senderID]]) {
149       if (debugTakeValues) {
150         NSArray *formValues = [_rq formValueKeys];
151         NSLog(@"%s: we are elem active (eid=%@): %@ ..", __PRETTY_FUNCTION__,
152               [_ctx elementID], formValues);
153       }
154       doTakeValues = YES;
155     }
156     else if (alwaysPassIn) {
157       // Note: this does not call the component! Bug? (see 'else' below)
158       if (debugTakeValues)
159         NSLog(@"%s: taking values from foreign request ",__PRETTY_FUNCTION__);
160       doTakeValues = YES;
161     }
162     else {
163       /* finally, let the component decide */
164       doTakeValues = [sComponent shouldTakeValuesFromRequest:_rq 
165                                  inContext:_ctx];
166       if (debugTakeValues) {
167         NSLog(@"%s: component should take values: %s ", __PRETTY_FUNCTION__,
168               doTakeValues ? "yes" : "no");
169       }
170     }
171     
172     if (doTakeValues) {
173       if (debugTakeValues) 
174         NSLog(@"%s: taking values ...", __PRETTY_FUNCTION__);
175       
176       [self->template takeValuesFromRequest:_rq inContext:_ctx];
177
178       if (debugTakeValues) 
179         NSLog(@"%s: did take values.", __PRETTY_FUNCTION__);
180     }
181     else if (debugTakeValues) {
182       [sComponent
183              debugWithFormat:
184                @"WOForm: *not* taking values from foreign request "
185                @"(id='%@' vs sid='%@') ...",
186                [_ctx elementID], [_ctx senderID]];
187     }
188   }
189   
190   if (![_ctx isInForm]) {
191     [[_ctx component]
192            errorWithFormat:@"(%s:%d): -isInForm is NO !!!",
193              __PRETTY_FUNCTION__, __LINE__];
194   }
195   else
196     [_ctx setInForm:NO];
197 }
198
199 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
200   id result = nil;
201   
202   [_ctx setInForm:YES];
203
204   if ([_ctx currentElementID] == nil) {
205     WOElement *element;
206     
207     if ((element = [_ctx activeFormElement])) {
208 #if 1
209       result = [self->template invokeActionForRequest:_rq inContext:_ctx];
210       RETAIN(result);
211 #else
212       /* wrong - need to setup correct component stack */
213       result = [[element invokeActionForRequest:_rq
214                          inContext:_ctx]
215                          retain];
216 #endif
217     }
218     else if (self->action) {
219       result = [self executeAction:self->action inContext:_ctx];
220     }
221     else if (self->pageName) {
222       NSString    *pname = nil;
223       WOComponent *page = nil;
224
225       pname = [self->pageName stringValueInComponent:[_ctx component]];
226       page  = [[_ctx application] pageWithName:pname inContext:_ctx];
227
228       if (page == nil) {
229         [[_ctx session] logWithFormat:
230                           @"%@[0x%p]: did not find page with name %@ !",
231                           NSStringFromClass([self class]), self, pname];
232       }
233       NSLog(@"showing page %@", page);
234       result = page;
235     }
236   }
237   else
238     result = [self->template invokeActionForRequest:_rq inContext:_ctx];
239
240   [_ctx setInForm:NO];
241
242   return result;
243 }
244
245 /* generate response */
246
247 - (NSString *)_addHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
248   /* post to a fixed hyperlink */
249   WOComponent *sComponent = [_ctx component];
250   NSString     *s;
251   NSDictionary *d;
252   
253   s = [self->href stringValueInComponent:sComponent];
254   d = [self->queryDictionary valueInComponent:sComponent];
255   
256   WOResponse_AddString(_r, s);
257   
258   return [self queryStringForQueryDictionary:d
259                andQueryParameters:self->queryParameters
260                inContext:_ctx];
261 }
262
263 - (NSString *)_addActionToResponse:(WOResponse *)_r inContext:(WOContext *)_c {
264   /* post to a component action */
265   NSDictionary *d;
266         
267   WOResponse_AddString(_r, [_c componentActionURL]);
268
269   d = [self->queryDictionary valueInComponent:[_c component]];
270   return [self queryStringForQueryDictionary:d
271                andQueryParameters:self->queryParameters
272                inContext:_c];
273 }
274
275 - (void)_addDirectActionToResponse:(WOResponse *)_r inContext:(WOContext *)_c {
276   /* a direct action link */
277   WOComponent *sComponent;
278   NSString            *daClass = nil;
279   NSString            *daName  = nil;
280   NSMutableDictionary *qd;
281   NSDictionary        *tmp;
282   NSString            *uri;
283           
284   sComponent = [_c component];
285   daClass = [self->actionClass      stringValueInComponent:sComponent];
286   daName  = [self->directActionName stringValueInComponent:sComponent];
287   
288   if (daClass != nil) {
289     if (daName != nil) {
290       if (![daClass isEqualToString:@"DirectAction"])
291         daName = [NSString stringWithFormat:@"%@/%@", daClass, daName];
292     }
293     else
294       daName = daClass;
295   }
296   
297   qd = [NSMutableDictionary dictionaryWithCapacity:16];
298   
299   /* add query dictionary */
300         
301   if (self->queryDictionary) {
302     if ((tmp = [self->queryDictionary valueInComponent:sComponent]))
303       [qd addEntriesFromDictionary:tmp];
304   }
305         
306   /* add ?style parameters */
307   
308   if (self->queryParameters) {
309     NSEnumerator *keys;
310     NSString     *key;
311   
312     keys = [self->queryParameters keyEnumerator];
313     while ((key = [keys nextObject])) {
314       id assoc, value;
315   
316       assoc = [self->queryParameters objectForKey:key];
317       value = [assoc stringValueInComponent:sComponent];
318       
319       [qd setObject:(value != nil ? value : (id)@"") forKey:key];
320     }
321   }
322         
323   /* add session ID */
324   if (self->sidInUrl && [_c hasSession]) {
325     WOSession *sn;
326
327     sn = [_c session];
328     [qd setObject:[sn sessionID] forKey:WORequestValueSessionID];
329             
330     if (![sn isDistributionEnabled]) {
331       [qd setObject:[[WOApplication application] number]
332           forKey:WORequestValueInstance];
333     }
334   }
335   else if (self->sidInUrl) {
336     /* Note: this is not a problem! Eg this occurs on the OGo Main component */
337     [self debugWithFormat:
338             @"Note: session-id is requested, but no session is active?"];
339   }
340   
341   uri = [_c directActionURLForActionNamed:daName queryDictionary:qd];
342   WOResponse_AddString(_r, uri);
343 }
344
345 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
346   WOComponent *sComponent;
347   NSString    *queryString = nil;
348   
349   if ([[_ctx request] isFromClientComponent]) {
350     [self->template appendToResponse:_response inContext:_ctx];
351     return;
352   }
353   
354   sComponent = [_ctx component];
355
356   if ([_ctx isInForm])
357     [self warnWithFormat:@"detected nested WOForm, component: %@", sComponent];
358   else
359     [_ctx setInForm:YES];
360
361   WOResponse_AddCString(_response, "<form action=\"");
362
363   /* add URL to response and return the query string */
364   
365   if (self->href != nil)
366     queryString = [self _addHrefToResponse:_response inContext:_ctx];
367   else if (self->directActionName != nil || self->actionClass != nil)
368     [self _addDirectActionToResponse:_response inContext:_ctx];
369   else
370     queryString = [self _addActionToResponse:_response inContext:_ctx];
371
372   if (self->fragmentIdentifier != nil) {
373     NSString *f = [self->fragmentIdentifier stringValueInComponent:sComponent];
374     if ([f isNotEmpty]) {
375       [_response appendContentCharacter:'#'];
376       WOResponse_AddString(_response, f);
377     }
378   }
379
380   /* append the query string */
381   
382   if (queryString != nil) {
383     [_response appendContentCharacter:'?'];
384     WOResponse_AddString(_response, queryString);
385   }
386   if (self->method != nil) {
387     WOResponse_AddCString(_response, "\" method=\"");
388     WOResponse_AddString(_response, 
389                          [self->method stringValueInComponent:sComponent]);
390     WOResponse_AddCString(_response, "\"");
391   }
392   else
393     WOResponse_AddCString(_response, "\" method=\"post\"");
394       
395   [self appendExtraAttributesToResponse:_response inContext:_ctx];
396   
397   if (self->otherTagString != nil) {
398     WOResponse_AddChar(_response, ' ');
399     WOResponse_AddString(_response,
400                          [self->otherTagString stringValueInComponent:
401                            sComponent]);
402   }
403   WOResponse_AddChar(_response, '>');
404
405   /* render form content */
406     
407   [self->template appendToResponse:_response inContext:_ctx];
408
409   /* close form */
410   
411   WOResponse_AddCString(_response, "</form>");
412   [_ctx setInForm:NO];
413 }
414
415 /* description */
416
417 - (NSString *)associationDescription {
418   NSMutableString *str = [NSMutableString stringWithCapacity:64];
419   //[str appendString:[super associationDescription]];
420
421   if (self->action)   [str appendFormat:@" action=%@", self->action];
422   if (self->href)     [str appendFormat:@" href=%@", self->href];
423   if (self->pageName) [str appendFormat:@" page=%@", self->pageName];
424
425   if (self->actionClass)
426     [str appendFormat:@" actionClass=%@", self->actionClass];
427   if (self->directActionName)
428     [str appendFormat:@" directAction=%@", self->directActionName];
429   if (self->template)
430     [str appendFormat:@" template=%@", self->template];
431   
432   return str;
433 }
434
435 @end /* WOForm */