]> err.no Git - sope/blob - sope-appserver/WEExtensions/WECollapsibleComponentContent.m
fix for SoProductResourceManager.m
[sope] / sope-appserver / WEExtensions / WECollapsibleComponentContent.m
1 /*
2   Copyright (C) 2000-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 <NGObjWeb/WODynamicElement.h>
24
25 @class WOAssociation;
26
27 @interface WECollapsibleComponentContent : WODynamicElement
28 {
29 @protected
30   WOAssociation *condition;
31   WOAssociation *visibility;
32   
33   WOAssociation *allowScript; /* perform clicks on browser (use javaScript) */
34   
35   WOElement     *template;
36 }
37 @end
38
39 @interface WECollapsibleAction : WODynamicElement
40 {
41 @protected
42   WOAssociation *openedImageFileName;
43   WOAssociation *closedImageFileName;
44   WOAssociation *framework;
45   WOAssociation *openedLabel;
46   WOAssociation *closedLabel;
47   WOAssociation *submitActionName;
48   WOAssociation *action; // if submit button, use submitActionName instead
49   WOAssociation *fragmentIdentifier;
50   WOAssociation *isClicked;
51
52   WOElement *template;
53 }
54 @end
55
56 #include <NGObjWeb/NGObjWeb.h>
57 #include "WEContextConditional.h"
58 #include "WEClientCapabilities.h"
59 #include "common.h"
60
61 static NSString *WECollapsible_TitleMode   = @"WECollapsible_TitleMode";
62 static NSString *WECollapsible_ContentMode = @"WECollapsible_ContentMode";
63 static NSString *WECollapsible_IsCollapsed = @"WECollapsible_IsCollapsed";
64 static NSString *WECollapsible_ScriptId    = @"WECollapsible_ScriptId";
65 static NSString *WECollapsible_HasScript   = @"WECollapsible_HasScript";
66 static NSString *Yes                       = @"YES";
67 static NSString *No                        = @"NO";
68
69 @implementation WECollapsibleComponentContent
70
71 - (id)initWithName:(NSString *)_name
72   associations:(NSDictionary *)_config
73   template:(WOElement *)_tmp
74 {
75   if ((self = [super initWithName:_name associations:_config template:_tmp])) {
76     self->condition   = WOExtGetProperty(_config, @"condition");
77     self->visibility  = WOExtGetProperty(_config, @"visibility");
78     self->allowScript = WOExtGetProperty(_config, @"allowScript");
79
80     if (self->visibility == nil)
81       NSLog(@"WARNING: WECollapsibleComponent 'visibility' not set");
82
83     if (self->visibility && ![self->visibility isValueSettable])
84       NSLog(@"WARNING: WECollapsibleComponent 'visibility' is not settable");
85     
86     self->template = [_tmp retain];
87   }
88   return self;
89 }
90
91 - (void)dealloc {
92   [self->condition   release];
93   [self->visibility  release];
94   [self->allowScript release];
95   [self->template    release];
96   [super dealloc];
97 }
98
99 /* responder */
100
101 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
102   BOOL isCollapsed;
103
104   isCollapsed = ![self->visibility boolValueInComponent:[_ctx component]];
105
106   // content
107   if (!isCollapsed) {
108     [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
109     [self->template takeValuesFromRequest:_req inContext:_ctx];
110     [_ctx removeObjectForKey:WECollapsible_ContentMode];
111   }
112
113   // title
114   [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
115
116   [self->template takeValuesFromRequest:_req inContext:_ctx];
117
118   [_ctx removeObjectForKey:WECollapsible_TitleMode];
119 }
120
121 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
122   id   result = nil;
123   BOOL isCollapsed;
124
125   isCollapsed = ![self->visibility boolValueInComponent:[_ctx component]];
126   
127   // title
128   [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
129   [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
130   [_ctx setObject:(isCollapsed) ? Yes : No forKey:WECollapsible_IsCollapsed];
131
132   result = [self->template invokeActionForRequest:_request inContext:_ctx];
133   isCollapsed = [[_ctx objectForKey:WECollapsible_IsCollapsed] boolValue];
134
135   if ([self->visibility isValueSettable])
136     [self->visibility setBoolValue:!isCollapsed inComponent:[_ctx component]];
137
138   [_ctx removeObjectForKey:WECollapsible_IsCollapsed];
139   [_ctx removeObjectForKey:WECollapsible_ContentMode];
140   [_ctx removeObjectForKey:WECollapsible_TitleMode];
141   
142   return result;
143 }
144
145 - (void)appendToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
146   WOComponent *comp;
147   BOOL        isCollapsed;
148   BOOL        doScript;
149   NSString    *scriptId;
150
151   comp     = [_ctx component];
152   doScript = [self->allowScript boolValueInComponent:comp];
153   scriptId = [[[_ctx elementID] componentsSeparatedByString:@"."]
154                      componentsJoinedByString:@"_"];
155
156   if (doScript) {
157     WEClientCapabilities *ccaps;
158     
159     ccaps    = [[_ctx request] clientCapabilities];
160     doScript = [ccaps isInternetExplorer];
161   }
162
163   if (doScript)
164     [_ctx setObject:scriptId forKey:WECollapsible_ScriptId];
165   
166   if ([self->visibility valueInComponent:comp] == nil) {
167     isCollapsed = ![self->condition boolValueInComponent:comp];
168     if ([self->visibility isValueSettable])
169       [self->visibility setBoolValue:!isCollapsed inComponent:comp];
170   }
171   else
172     isCollapsed = ![self->visibility boolValueInComponent:comp];
173
174   // append title
175   [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
176   [_ctx setObject:(isCollapsed) ? Yes : No forKey:WECollapsible_IsCollapsed];
177
178   [self->template appendToResponse:_resp inContext:_ctx];
179   
180   [_ctx removeObjectForKey:WECollapsible_IsCollapsed];
181   [_ctx removeObjectForKey:WECollapsible_TitleMode];
182   
183   // append content
184   if (!isCollapsed || doScript) {
185     [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
186     if (doScript) {
187       [_resp appendContentString:@"<div class=\"collapsible\" "];
188       [_resp appendContentString:@"id=\"collapsible"];
189       [_resp appendContentString:scriptId];
190       [_resp appendContentString:@"\" style=\"display:"];
191       [_resp appendContentString:(isCollapsed) ? @"none" : @"block"];
192       [_resp appendContentString:@";\">"];
193     }
194     
195     [self->template appendToResponse:_resp inContext:_ctx];
196     
197     if (doScript)
198       [_resp appendContentString:@"</div>"];
199     [_ctx removeObjectForKey:WECollapsible_ContentMode];
200   }
201   [_ctx removeObjectForKey:WECollapsible_ScriptId];
202 }
203 @end /* WECollapsibleComponentContent */
204
205 @implementation WECollapsibleAction
206
207 - (id)initWithName:(NSString *)_name
208   associations:(NSDictionary *)_config
209   template:(WOElement *)_temp
210 {
211   if ((self = [super initWithName:_name associations:_config template:_temp])) {
212     self->openedImageFileName = WOExtGetProperty(_config, @"openedImageFileName");
213     self->closedImageFileName = WOExtGetProperty(_config, @"closedImageFileName");
214     self->framework           = WOExtGetProperty(_config, @"framework");
215     self->openedLabel         = WOExtGetProperty(_config, @"openedLabel");
216     self->closedLabel         = WOExtGetProperty(_config, @"closedLabel");
217     self->submitActionName    = WOExtGetProperty(_config, @"submitActionName");
218     self->action              = WOExtGetProperty(_config, @"action");
219     self->fragmentIdentifier  = WOExtGetProperty(_config, @"fragmentIdentifier");
220     self->isClicked           = WOExtGetProperty(_config, @"isClicked");
221
222     self->template = [_temp retain];
223   }
224   return self;
225 }
226
227 - (void)dealloc {
228   [self->openedImageFileName release];
229   [self->closedImageFileName release];
230   [self->framework           release];
231   [self->openedLabel         release];
232   [self->closedLabel         release];
233   [self->submitActionName    release];
234   [self->action              release];
235   [self->fragmentIdentifier  release];
236   [self->isClicked           release];
237   [self->template            release];
238   [super dealloc];
239 }
240
241 /* requests */
242
243 - (void)takeValuesFromRequest:(WORequest *)_request
244   inContext:(WOContext *)_ctx
245 {
246   NSString *eid;
247
248   eid = [_ctx elementID];
249
250   if ([_request formValueForKey:[eid stringByAppendingString:@".c.x"]]) {
251     [_ctx addActiveFormElement:self];
252     [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".c"]];
253   }
254   else if ([_request formValueForKey:[eid stringByAppendingString:@".e.x"]]) {
255     [_ctx addActiveFormElement:self];
256     [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".e"]];
257   }
258 }
259
260 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
261   NSString    *state;
262   BOOL        doForm;
263   
264   state = [[_ctx currentElementID] stringValue];
265   
266   doForm = ([_ctx isInForm] && self->submitActionName &&
267             ([self->submitActionName valueInComponent:[_ctx component]]));
268   
269   [_ctx consumeElementID]; // consume state-id
270     
271   if ([state isEqualToString:@"e"]) {
272     [_ctx setObject:[NSNumber numberWithBool:YES]
273           forKey:WECollapsible_IsCollapsed];
274     
275     if (doForm)
276       [self->submitActionName valueInComponent:[_ctx component]];
277     else if ([self->action valueInComponent:[_ctx component]] != nil)
278       [self->action valueInComponent:[_ctx component]];
279   }
280   else if ([state isEqualToString:@"c"]) {
281     [_ctx setObject:[NSNumber numberWithBool:NO]
282           forKey:WECollapsible_IsCollapsed];
283     
284     if (doForm)
285       [self->submitActionName valueInComponent:[_ctx component]];
286     else if ([self->action valueInComponent:[_ctx component]] != nil)
287       [self->action valueInComponent:[_ctx component]];
288   }
289   if ([self->isClicked isValueSettable])
290     [self->isClicked setBoolValue:YES inComponent:[_ctx component]];
291     
292   return nil; 
293 }
294
295 - (void)_appendScriptWithID:(NSString *)scriptId 
296   label:(NSString *)label imageURL:(NSString *)img
297   toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx 
298 {
299   WOComponent *comp;
300     NSString *openedImg  = nil;
301     NSString *closedImg  = nil;
302     NSString *openLabel  = nil;
303     NSString *closeLabel = nil;
304
305     comp       = [_ctx component];
306     closedImg  = [self->closedImageFileName stringValueInComponent:comp];
307     openedImg  = [self->openedImageFileName stringValueInComponent:comp];
308     openLabel  = [self->openedLabel         stringValueInComponent:comp];
309     closeLabel = [self->closedLabel         stringValueInComponent:comp];
310
311     closedImg = WEUriOfResource(closedImg, _ctx);
312     openedImg = WEUriOfResource(openedImg, _ctx);
313
314     if (![_ctx objectForKey:WECollapsible_HasScript]) {
315       [_resp appendContentString:
316            @"\n<script language=\"JavaScript\">\n"
317            @"<!--\n"
318            @"function toggleColl(el, img1, img2, alt1, alt2)\n"
319            @"{\n"
320            @"   whichEl = eval(\"collapsible\" + el);\n"
321            @"   whichIm = event.srcElement;\n"
322            @"   if (whichEl.style.display == \"none\") {\n"
323            @"           whichEl.style.display = \"block\";\n"
324            @"           whichIm.src = img1;\n"
325            @"       whichIm.alt = alt1;\n"
326            @"   }\n"
327            @"   else {\n"
328            @"           whichEl.style.display = \"none\";\n"
329            @"       whichIm.src = img2;\n"
330            @"       whichIm.alt = alt2;\n"
331            @"   }\n"
332            @"}\n"
333            @"//-->\n"
334            @"</script>\n"];
335       [_ctx setObject:Yes forKey:WECollapsible_HasScript];
336     }
337     
338     [_resp appendContentString:@"<a href=\"#\" onclick=\"toggleColl('"];
339     [_resp appendContentString:scriptId];
340     [_resp appendContentString:@"','"];
341     [_resp appendContentHTMLString:openedImg];
342     [_resp appendContentString:@"','"];
343     [_resp appendContentHTMLString:closedImg];
344     [_resp appendContentString:@"','"];
345     [_resp appendContentHTMLString:openLabel];
346     [_resp appendContentString:@"','"];
347     [_resp appendContentHTMLString:closeLabel];
348     [_resp appendContentString:@"'); return false\"><img "];
349     if (label) {
350       [_resp appendContentString:@"alt=\""];
351       [_resp appendContentString:label];
352       [_resp appendContentString:@"\" title=\""];
353       [_resp appendContentString:label];
354       [_resp appendContentString:@"\" "];
355     }
356     [_resp appendContentString:@"border=\"0\" name=\"imEx\" src=\""];
357     [_resp appendContentString:img];
358     [_resp appendContentString:@"\" /></a>"];
359 }
360
361 - (void)_appendLinkWithFragmentID:(NSString *)fragId
362   label:(NSString *)label imageURL:(NSString *)img
363   isCollapsed:(BOOL)isCollapsed
364   toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx 
365 {
366   [_resp appendContentString:@"<a href=\""];
367   [_resp appendContentString:[_ctx componentActionURL]];
368   if ([fragId length] > 0) {
369     [_resp appendContentString:@"#"];
370     [_resp appendContentString:fragId];
371   }
372   [_resp appendContentString:@"\">"];
373   
374   if (img) {
375     [_resp appendContentString:@"<img border=\"0\" src=\""];
376     [_resp appendContentString:img];
377     [_resp appendContentString:@"\""];
378     if (label) {
379       [_resp appendContentString:@" alt=\""];
380       [_resp appendContentString:label];
381       [_resp appendContentString:@"\" title=\""];
382       [_resp appendContentString:label];
383       [_resp appendContentString:@"\""];
384     }
385     [_resp appendContentString:@" />"];
386   }
387   else
388     [_resp appendContentString:(isCollapsed) ? @"[+]" : @"[-]"];
389     
390   [_resp appendContentString:@"</a>"];
391 }
392
393 - (void)appendToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
394   /* TODO: split up this huge method */
395   BOOL        isCollapsed;
396   BOOL        doForm;
397   BOOL        doScript;
398   WOComponent *comp;
399   NSString    *img      = nil;
400   NSString    *label    = nil;
401   NSString    *fragId   = nil;
402   NSString    *scriptId = nil;
403   
404   comp   = [_ctx component];
405   fragId = [self->fragmentIdentifier stringValueInComponent:comp];
406
407   isCollapsed = [[_ctx objectForKey:WECollapsible_IsCollapsed] boolValue];
408   scriptId    = [_ctx objectForKey:WECollapsible_ScriptId];
409   doScript    = [scriptId length] > 0 ? YES : NO;
410   
411   img = (isCollapsed)
412     ? [self->closedImageFileName stringValueInComponent:comp]
413     : [self->openedImageFileName stringValueInComponent:comp];
414
415   label = (isCollapsed)
416     ? [self->closedLabel stringValueInComponent:comp]
417     : [self->openedLabel stringValueInComponent:comp];
418
419   img = WEUriOfResource(img, _ctx);
420
421   /*
422   if (isCollapsed)
423     [_resp appendContentString:@"&nbsp;"];
424   */
425
426   doForm = ([_ctx isInForm] && self->submitActionName && img);
427
428   [_ctx appendElementIDComponent:(isCollapsed) ? @"c" : @"e"];
429   if (doScript) {
430     [self _appendScriptWithID:scriptId label:label imageURL:img
431           toResponse:_resp inContext:_ctx];
432   }
433   else if (doForm) {
434     [_resp appendContentString:@"<input type=\"image\" border=\"0\" name=\""];
435     [_resp appendContentString:[_ctx elementID]];
436     [_resp appendContentString:@"\" src=\""];
437     [_resp appendContentString:img];
438     [_resp appendContentString:@"\" />"];
439   }
440   else {
441     [self _appendLinkWithFragmentID:fragId label:label imageURL:img
442           isCollapsed:isCollapsed
443           toResponse:_resp inContext:_ctx];
444   }
445   
446   if (fragId) {
447     WEClientCapabilities *ccaps;
448     
449     ccaps    = [[_ctx request] clientCapabilities];
450     
451     [_resp appendContentString:@"<a name=\""];
452     [_resp appendContentString:fragId];
453     [_resp appendContentString:@"\">&nbsp;</a>"];
454     if ([self->isClicked boolValueInComponent:comp] &&
455         ([ccaps isInternetExplorer] || [ccaps isMozilla] || [ccaps isNetscape6])) {
456       if ([self->isClicked isValueSettable])
457         [self->isClicked setBoolValue:NO inComponent:comp];
458       
459       [_resp appendContentString:
460              [NSString stringWithFormat:
461              @"\n<script language=\"JavaScript\">\n"
462              @"<!--\n"
463              @"  window.location.hash=\"#%@\";\n"
464              @"//-->\n"
465              @"</script>\n",
466              fragId]];
467     }
468   }
469   else
470     [_resp appendContentString:@"&nbsp;"];
471   
472   if (label && !doScript) {
473     if (!doForm) {
474       [_resp appendContentString:@"<a href=\""];
475       [_resp appendContentString:[_ctx componentActionURL]];
476       if (fragId) {
477         [_resp appendContentString:@"#"];
478         [_resp appendContentString:fragId];
479       }
480       [_resp appendContentString:@"\">"];
481     }
482     
483     [_resp appendContentString:label];
484
485     if (!doForm)
486       [_resp appendContentString:@"</a>"];
487   }
488   [_ctx deleteLastElementIDComponent];
489 }
490
491 @end /* WECollapsibleAction */
492
493 @interface WECollapsibleTitleMode: WEContextConditional
494 @end
495
496 @implementation WECollapsibleTitleMode
497
498 - (NSString *)_contextKey {
499   return WECollapsible_TitleMode;
500 }
501
502 @end /* WECollapsibleTitleMode */
503
504 @interface WECollapsibleContentMode: WEContextConditional
505 @end
506
507 @implementation WECollapsibleContentMode
508
509 - (NSString *)_contextKey {
510   return WECollapsible_ContentMode;
511 }
512
513 @end /* WECollapsibleContentMode */