]> err.no Git - sope/blob - sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m
renamed packages as discussed in the developer list
[sope] / sope-appserver / NGObjWeb / DynamicElements / _WOComplexHyperlink.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 "WOHyperlink.h"
24
25 /*
26   Class Hierachy
27     [WOHTMLDynamicElement]
28       [WOHyperlink]
29         _WOComplexHyperlink
30           _WOHrefHyperlink
31           _WOActionHyperlink
32           _WOPageHyperlink
33           _WODirectActionHyperlink
34 */
35
36 @interface _WOComplexHyperlink : WOHyperlink
37 {
38   /* superclass of most hyperlink classes */
39 @protected
40   WOAssociation *fragmentIdentifier;
41   WOAssociation *string;
42   WOAssociation *target;
43   WOAssociation *disabled;
44   WOElement     *template;
45   
46   /* new in WO4: */
47   WOAssociation *queryDictionary;
48   NSDictionary  *queryParameters;  /* associations beginning with ? */
49
50   /* non WO, image stuff */
51   WOAssociation *filename;         /* path relative to WebServerResources */
52   WOAssociation *framework;
53   WOAssociation *src;              /* absolute URL */
54   WOAssociation *disabledFilename; /* icon for 'disabled' state */
55 }
56
57 - (NSString *)associationDescription;
58
59 @end
60
61 @interface _WOHrefHyperlink : _WOComplexHyperlink
62 {
63   WOAssociation *href;
64 }
65 @end
66
67 @interface _WOActionHyperlink : _WOComplexHyperlink
68 {
69   WOAssociation *action;
70 }
71 @end
72
73 @interface _WOPageHyperlink : _WOComplexHyperlink
74 {
75   WOAssociation *pageName;
76 }
77 @end
78
79 @interface _WODirectActionHyperlink : _WOComplexHyperlink
80 {
81   WOAssociation *actionClass;
82   WOAssociation *directActionName;
83   BOOL          sidInUrl;          /* include session-id in wa URL ? */
84 }
85 @end
86
87 #include "WOElement+private.h"
88 #include "WOHyperlinkInfo.h"
89 #include "WOCompoundElement.h"
90 #include <NGObjWeb/WOApplication.h>
91 #include <NGObjWeb/WOResourceManager.h>
92 #include <NGExtensions/NSString+Ext.h>
93 #include "common.h"
94
95 static Class NSURLClass = Nil;
96
97 @implementation _WOComplexHyperlink
98
99 + (int)version {
100   return [super version] /* v4 */;
101 }
102 + (void)initialize {
103   NSAssert2([super version] == 4,
104             @"invalid superclass (%@) version %i !",
105             NSStringFromClass([self superclass]), [super version]);
106   if (NSURLClass == Nil)
107     NSURLClass = [NSURL class];
108 }
109
110 - (id)initWithName:(NSString *)_name
111   hyperlinkInfo:(WOHyperlinkInfo *)_info
112   template:(WOElement *)_t
113 {
114   if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
115     self->template = [_t retain];
116     
117     self->fragmentIdentifier = _info->fragmentIdentifier;
118     self->string             = _info->string;
119     self->target             = _info->target;
120     self->disabled           = _info->disabled;
121     self->queryDictionary    = _info->queryDictionary;
122     self->queryParameters    = _info->queryParameters;
123     
124     /* image */
125     self->filename         = _info->filename;
126     self->framework        = _info->framework;
127     self->src              = _info->src;
128     self->disabledFilename = _info->disabledFilename;
129     
130     self->containsForm = self->queryParameters ? YES : NO;
131   }
132   return self;
133 }
134
135 - (void)dealloc {
136   [self->template           release];
137   [self->queryDictionary    release];
138   [self->queryParameters    release];
139   [self->disabledFilename   release];
140   [self->filename           release];
141   [self->framework          release];
142   [self->src                release];
143   [self->fragmentIdentifier release];
144   [self->string             release];
145   [self->target             release];
146   [self->disabled           release];
147   [super dealloc];
148 }
149
150 /* accessors */
151
152 - (WOElement *)template {
153   return self->template;
154 }
155
156 // ******************** responder ********************
157
158 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
159   /* links can take form values !!!! (for query-parameters) */
160   
161   if (self->queryParameters) {
162     /* apply values to ?style parameters */
163     WOComponent  *sComponent = [_ctx component];
164     NSEnumerator *keys;
165     NSString     *key;
166
167     keys = [self->queryParameters keyEnumerator];
168     while ((key = [keys nextObject])) {
169       id assoc, value;
170
171       assoc = [self->queryParameters objectForKey:key];
172       
173       if ([assoc isValueSettable]) {
174         value = [_req formValueForKey:key];
175         [assoc setValue:value inComponent:sComponent];
176       }
177     }
178   }
179   
180   [self->template takeValuesFromRequest:_req inContext:_ctx];
181 }
182
183 - (id)invokeActionForRequest:(WORequest *)_request
184   inContext:(WOContext *)_ctx
185 {
186   if (self->disabled) {
187     if ([self->disabled boolValueInComponent:[_ctx component]])
188       return nil;
189   }
190   
191   if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
192     /* link is not the active element */
193     return [self->template invokeActionForRequest:_request inContext:_ctx];
194   
195   /* link is active */
196   [[_ctx session] logWithFormat:@"%@[0x%08X]: no action/page set !",
197                   NSStringFromClass([self class]), self];
198   return nil;
199 }
200
201 - (BOOL)_appendHrefToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
202   [self subclassResponsibility:_cmd];
203   return NO;
204 }
205
206 - (void)_addImageToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
207   WOComponent *sComponent = [_ctx component];
208   NSString *uUri;
209   NSString *uFi  = nil;
210   NSArray *languages;
211   
212   uUri = [[self->src valueInContext:_ctx] stringValue];
213       
214   if ([self->disabled boolValueInComponent:sComponent]) {
215     uFi =  [self->disabledFilename stringValueInComponent:sComponent];
216     if (uFi == nil)
217       uFi = [self->filename stringValueInComponent:sComponent];
218   }
219   else
220     uFi = [self->filename stringValueInComponent:sComponent];
221   
222   if (!((uFi != nil) || (uUri != nil))) 
223     return;
224
225   languages = [_ctx hasSession]
226     ? [[_ctx session] languages]
227     : [[_ctx request] browserLanguages];
228         
229   WOResponse_AddCString(_resp, "<img src=\"");
230   
231   if (uFi) {
232     WOResourceManager *rm;
233           
234     if ((rm = [[_ctx component] resourceManager]) == nil)
235       rm = [[_ctx application] resourceManager];
236           
237     uFi = [rm urlForResourceNamed:uFi
238               inFramework:
239                 [self->framework stringValueInComponent:sComponent]
240               languages:languages
241               request:[_ctx request]];
242     if (uFi == nil) {
243       NSLog(@"%@: did not find resource %@", sComponent,
244             [self->filename stringValueInComponent:sComponent]);
245       uFi = uUri;
246     }
247     [_resp appendContentHTMLAttributeValue:uFi];
248   }
249   else {
250     [_resp appendContentHTMLAttributeValue:uUri];
251   }
252   WOResponse_AddChar(_resp, '"');
253   
254   [self appendExtraAttributesToResponse:_resp inContext:_ctx];
255   WOResponse_AddCString(_resp, " />");
256 }
257
258 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
259   WOComponent *sComponent = [_ctx component];
260   NSString *content;
261   NSString *targetView;
262   NSString *queryString = nil;
263
264   if ([[_ctx request] isFromClientComponent])
265     return;
266   
267   content    = [self->string valueInContext:_ctx];
268   targetView = [self->target stringValueInComponent:sComponent];
269     
270   WOResponse_AddCString(_response, "<a href=\"");
271     
272   if ([self _appendHrefToResponse:_response inContext:_ctx]) {
273       queryString = [self queryStringForQueryDictionary:
274                             [self->queryDictionary valueInComponent:sComponent]
275                           andQueryParameters:self->queryParameters
276                           inContext:_ctx];
277   }
278   
279   if (self->fragmentIdentifier) {
280       [_response appendContentCharacter:'#'];
281       WOResponse_AddString(_response,
282          [self->fragmentIdentifier stringValueInComponent:sComponent]);
283   }
284   if (queryString) {
285     [_response appendContentCharacter:'?'];
286     WOResponse_AddString(_response, queryString);
287   }
288   [_response appendContentCharacter:'"'];
289     
290   if (targetView) {
291     WOResponse_AddCString(_response, " target=\"");
292     WOResponse_AddString(_response, targetView);
293     [_response appendContentCharacter:'"'];
294   }
295     
296   [self appendExtraAttributesToResponse:_response inContext:_ctx];
297     
298   if (self->otherTagString) {
299     NSString *s;
300     
301     s = [self->otherTagString stringValueInComponent:[_ctx component]];
302     WOResponse_AddString(_response, s);
303   }
304   [_response appendContentCharacter:'>'];
305     
306   /* content */
307   [self->template appendToResponse:_response inContext:_ctx];
308   if (content) [_response appendContentHTMLString:content];
309   
310   /* image content */
311   if ((self->src != nil) || (self->filename != nil))
312     [self _addImageToResponse:_response inContext:_ctx];
313   
314   /* closing tag */
315   WOResponse_AddCString(_response, "</a>");
316 }
317
318 /* description */
319
320 - (NSString *)associationDescription {
321   NSMutableString *str = [NSMutableString stringWithCapacity:256];
322
323   if (self->fragmentIdentifier)
324     [str appendFormat:@" fragment=%@", self->fragmentIdentifier];
325   if (self->string)   [str appendFormat:@" string=%@",   self->string];
326   if (self->target)   [str appendFormat:@" target=%@",   self->target];
327   if (self->disabled) [str appendFormat:@" disabled=%@", self->disabled];
328
329   /* image .. */
330   if (self->filename)  [str appendFormat:@" filename=%@",  self->filename];
331   if (self->framework) [str appendFormat:@" framework=%@", self->framework];
332   if (self->src)       [str appendFormat:@" src=%@",       self->src];
333
334   return str;
335 }
336
337 @end /* _WOComplexHyperlink */
338
339 @implementation _WOHrefHyperlink
340
341 static BOOL debugStaticLinks = NO;
342
343 + (void)initialize {
344   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
345   
346   debugStaticLinks = [ud boolForKey:@"WODebugStaticLinkProcessing"];
347 }
348
349 - (id)initWithName:(NSString *)_name
350   hyperlinkInfo:(WOHyperlinkInfo *)_info
351   template:(WOElement *)_t
352 {
353   if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
354     self->href = _info->href;
355   }
356   return self;
357 }
358
359 - (void)dealloc {
360   [self->href release];
361   [super dealloc];
362 }
363
364 /* URI generation */
365
366 - (BOOL)_appendHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
367   NSString *s;
368   id    hrefValue;
369   NSURL *url, *base;
370   
371   base      = [_ctx baseURL];
372   hrefValue = [self->href valueInContext:_ctx];
373   url       = hrefValue;
374   
375   if (hrefValue == nil)
376     return NO;
377    
378   if ([hrefValue isKindOfClass:NSURLClass]) {
379     s = [hrefValue stringValueRelativeToURL:base];
380   }
381   else {
382     s = [hrefValue stringValue];
383     
384     if (!([s hasPrefix:@"mailto:"] || [s hasPrefix:@"javascript:"])) {
385       if ([s isAbsoluteURL]) {
386         // TODO: why are we doing this? we could just pass through the string?
387         //    => probably to generate relative links
388         url = [NSURLClass URLWithString:s];
389       }
390       else if (base != nil) {
391         url = [NSURLClass URLWithString:s relativeToURL:base];
392       }
393       else {
394         [self logWithFormat:@"WARNING: missing base URL in context ..."];
395         WOResponse_AddString(_r, s);
396         return YES;
397       }
398       
399       if (url == nil) {
400         [self logWithFormat:
401                 @"couldn't construct URL from 'href' string '%@' (base=%@)",
402                 s, base];
403         return NO;
404       }
405       s = [url stringValueRelativeToURL:base];
406     }
407   }
408   
409   /* generate URL */
410   
411   if (debugStaticLinks) {
412     [self logWithFormat:@"static links based on 'href': '%@'", hrefValue];
413     [self logWithFormat:@"  base     %@", base];
414     [self logWithFormat:@"  base-abs %@", [base absoluteString]];
415     [self logWithFormat:@"  url      %@", url];
416     [self logWithFormat:@"  url-abs  %@", [url absoluteString]];
417     [self logWithFormat:@"  string   %@", s];
418   }
419   WOResponse_AddString(_r, s);
420   return YES;
421 }
422
423 /* description */
424
425 - (NSString *)associationDescription {
426   NSMutableString *str = [NSMutableString stringWithCapacity:256];
427
428   [str appendFormat:@" href=%@", self->href];
429   [str appendString:[super associationDescription]];
430   
431   return str;
432 }
433
434 @end /* _WOHrefHyperlink */
435
436 @implementation _WOActionHyperlink
437
438 - (id)initWithName:(NSString *)_name
439   hyperlinkInfo:(WOHyperlinkInfo *)_info
440   template:(WOElement *)_t
441 {
442   if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
443     self->action = _info->action;
444   }
445   return self;
446 }
447
448 - (void)dealloc {
449   [self->action release];
450   [super dealloc];
451 }
452
453 /* dynamic invocation */
454
455 - (id)invokeActionForRequest:(WORequest *)_request
456   inContext:(WOContext *)_ctx
457 {
458   if (self->disabled) {
459     if ([self->disabled boolValueInComponent:[_ctx component]])
460       return nil;
461   }
462
463   if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
464     /* link is not the active element */
465     return [self->template invokeActionForRequest:_request inContext:_ctx];
466   
467   /* link is active */
468   return [self executeAction:self->action inContext:_ctx];
469 }
470
471 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
472   inContext:(WOContext *)_ctx
473 {
474   WOResponse_AddString(_response, [_ctx componentActionURL]);
475   return YES;
476 }
477
478 /* description */
479
480 - (NSString *)associationDescription {
481   NSMutableString *str = [NSMutableString stringWithCapacity:256];
482
483   [str appendFormat:@" action=%@", self->action];
484   [str appendString:[super associationDescription]];
485   return str;
486 }
487
488 @end /* _WOActionHyperlink */
489
490 @implementation _WOPageHyperlink
491
492 - (id)initWithName:(NSString *)_name
493   hyperlinkInfo:(WOHyperlinkInfo *)_info
494   template:(WOElement *)_t
495 {
496   if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
497     self->pageName = _info->pageName;
498   }
499   return self;
500 }
501
502 - (void)dealloc {
503   [self->pageName release];
504   [super dealloc];
505 }
506
507 /* actions */
508
509 - (id)invokeActionForRequest:(WORequest *)_request
510   inContext:(WOContext *)_ctx
511 {
512   WOComponent *page;
513   NSString    *name;
514
515   if (self->disabled) {
516     if ([self->disabled boolValueInComponent:[_ctx component]])
517       return nil;
518   }
519   
520   if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
521     /* link is not the active element */
522     return [self->template invokeActionForRequest:_request inContext:_ctx];
523   
524   /* link is the active element */
525   
526   name = [self->pageName stringValueInComponent:[_ctx component]];
527   page = [[_ctx application] pageWithName:name inContext:_ctx];
528
529   if (page == nil) {
530     [[_ctx session] logWithFormat:
531                       @"%@[0x%08X]: did not find page with name %@ !",
532                       NSStringFromClass([self class]), self, name];
533   }
534   return page;
535 }
536
537 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
538   inContext:(WOContext *)_ctx
539 {
540   /*
541     Profiling:
542       87% -componentActionURL
543       13% NSString dataUsingEncoding(appendContentString!)
544     TODO(prof): use addcstring
545   */
546   WOResponse_AddString(_response, [_ctx componentActionURL]);
547   return YES;
548 }
549
550 /* description */
551
552 - (NSString *)associationDescription {
553   NSMutableString *str = [NSMutableString stringWithCapacity:256];
554
555   [str appendFormat:@" pageName=%@", self->pageName];
556   [str appendString:[super associationDescription]];
557   return str;
558 }
559
560 @end /* _WOPageHyperlink */
561
562 @implementation _WODirectActionHyperlink
563
564 - (id)initWithName:(NSString *)_name
565   hyperlinkInfo:(WOHyperlinkInfo *)_info
566   template:(WOElement *)_t
567 {
568   if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
569     self->actionClass      = _info->actionClass;
570     self->directActionName = _info->directActionName;
571     self->sidInUrl         = _info->sidInUrl;
572
573     self->containsForm = NO; /* direct actions are never form stuff ... */
574   }
575   return self;
576 }
577
578 - (void)dealloc {
579   [self->actionClass      release];
580   [self->directActionName release];
581   [super dealloc];
582 }
583
584 /* href */
585
586 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
587   /* DA links can *never* take form values !!!! */
588   [self->template takeValuesFromRequest:_req inContext:_ctx];
589 }
590
591 - (id)invokeActionForRequest:(WORequest *)_request
592   inContext:(WOContext *)_ctx
593 {
594   /* DA links can *never* invoke an action !!!! */
595   return [self->template invokeActionForRequest:_request inContext:_ctx];
596 }
597
598 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
599   inContext:(WOContext *)_ctx
600 {
601   WOComponent         *sComponent;
602   NSString            *daClass;
603   NSString            *daName;
604   NSMutableDictionary *qd;
605   NSDictionary        *tmp;
606
607   sComponent = [_ctx component];
608   daClass = [self->actionClass stringValueInComponent:sComponent];
609   daName  = [self->directActionName stringValueInComponent:sComponent];
610
611   if (daClass) {
612     if (daName) {
613       if (![daClass isEqualToString:@"DirectAction"])
614         daName = [NSString stringWithFormat:@"%@/%@", daClass, daName];
615     }
616     else
617       daName = daClass;
618   }
619   
620   qd = [NSMutableDictionary dictionaryWithCapacity:16];
621   
622   /* add query dictionary */
623   
624   if (self->queryDictionary) {
625     if ((tmp = [self->queryDictionary valueInComponent:sComponent]))
626       [qd addEntriesFromDictionary:tmp];
627   }
628   
629   /* add ?style parameters */
630
631   if (self->queryParameters) {
632     NSEnumerator *keys;
633     NSString     *key;
634
635     keys = [self->queryParameters keyEnumerator];
636     while ((key = [keys nextObject])) {
637       id assoc, value;
638
639       assoc = [self->queryParameters objectForKey:key];
640       value = [assoc stringValueInComponent:sComponent];
641           
642       [qd setObject:(value ? value : @"") forKey:key];
643     }
644   }
645       
646   /* add session ID */
647   
648   if (self->sidInUrl) {
649     if ([_ctx hasSession]) {
650       WOSession *sn;
651       
652       sn = [_ctx session];
653       [qd setObject:[sn sessionID] forKey:WORequestValueSessionID];
654       
655       if (![sn isDistributionEnabled]) {
656         [qd setObject:[[WOApplication application] number]
657             forKey:WORequestValueInstance];
658       }
659     }
660   }
661
662   WOResponse_AddString(_response,
663                        [_ctx directActionURLForActionNamed:daName
664                              queryDictionary:qd]);
665   return NO;
666 }
667
668 /* description */
669
670 - (NSString *)associationDescription {
671   NSMutableString *str = [NSMutableString stringWithCapacity:256];
672
673   if (self->actionClass)
674     [str appendFormat:@" actionClass=%@", self->actionClass];
675   if (self->directActionName)
676     [str appendFormat:@" directAction=%@", self->directActionName];
677   
678   [str appendString:[super associationDescription]];
679   return str;
680 }
681
682 @end /* _WODirectActionHyperlink */