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