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