2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
23 #include "WOHyperlink.h"
27 [WOHTMLDynamicElement]
33 _WODirectActionHyperlink
36 @interface _WOComplexHyperlink : WOHyperlink
38 /* superclass of most hyperlink classes */
40 WOAssociation *fragmentIdentifier;
41 WOAssociation *string;
42 WOAssociation *target;
43 WOAssociation *disabled;
47 WOAssociation *queryDictionary;
48 NSDictionary *queryParameters; /* associations beginning with ? */
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 */
57 - (NSString *)associationDescription;
61 @interface _WOHrefHyperlink : _WOComplexHyperlink
67 @interface _WOActionHyperlink : _WOComplexHyperlink
69 WOAssociation *action;
73 @interface _WOPageHyperlink : _WOComplexHyperlink
75 WOAssociation *pageName;
79 @interface _WODirectActionHyperlink : _WOComplexHyperlink
81 WOAssociation *actionClass;
82 WOAssociation *directActionName;
83 BOOL sidInUrl; /* include session-id in wa URL ? */
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>
95 static Class NSURLClass = Nil;
97 @implementation _WOComplexHyperlink
100 return [super version] /* v4 */;
103 NSAssert2([super version] == 4,
104 @"invalid superclass (%@) version %i !",
105 NSStringFromClass([self superclass]), [super version]);
106 if (NSURLClass == Nil)
107 NSURLClass = [NSURL class];
110 - (id)initWithName:(NSString *)_name
111 hyperlinkInfo:(WOHyperlinkInfo *)_info
112 template:(WOElement *)_t
114 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
115 self->template = [_t retain];
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;
125 self->filename = _info->filename;
126 self->framework = _info->framework;
127 self->src = _info->src;
128 self->disabledFilename = _info->disabledFilename;
130 self->containsForm = self->queryParameters ? YES : NO;
136 [self->template release];
137 [self->queryDictionary release];
138 [self->queryParameters release];
139 [self->disabledFilename release];
140 [self->filename release];
141 [self->framework release];
143 [self->fragmentIdentifier release];
144 [self->string release];
145 [self->target release];
146 [self->disabled release];
152 - (WOElement *)template {
153 return self->template;
156 // ******************** responder ********************
158 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
159 /* links can take form values !!!! (for query-parameters) */
161 if (self->queryParameters) {
162 /* apply values to ?style parameters */
163 WOComponent *sComponent = [_ctx component];
167 keys = [self->queryParameters keyEnumerator];
168 while ((key = [keys nextObject])) {
171 assoc = [self->queryParameters objectForKey:key];
173 if ([assoc isValueSettable]) {
174 value = [_req formValueForKey:key];
175 [assoc setValue:value inComponent:sComponent];
180 [self->template takeValuesFromRequest:_req inContext:_ctx];
183 - (id)invokeActionForRequest:(WORequest *)_request
184 inContext:(WOContext *)_ctx
186 if (self->disabled) {
187 if ([self->disabled boolValueInComponent:[_ctx component]])
191 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
192 /* link is not the active element */
193 return [self->template invokeActionForRequest:_request inContext:_ctx];
196 [[_ctx session] logWithFormat:@"%@[0x%08X]: no action/page set !",
197 NSStringFromClass([self class]), self];
201 - (BOOL)_appendHrefToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
202 [self subclassResponsibility:_cmd];
206 - (void)_addImageToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
207 WOComponent *sComponent = [_ctx component];
212 uUri = [[self->src valueInContext:_ctx] stringValue];
214 if ([self->disabled boolValueInComponent:sComponent]) {
215 uFi = [self->disabledFilename stringValueInComponent:sComponent];
217 uFi = [self->filename stringValueInComponent:sComponent];
220 uFi = [self->filename stringValueInComponent:sComponent];
222 if (!((uFi != nil) || (uUri != nil)))
225 languages = [_ctx hasSession]
226 ? [[_ctx session] languages]
227 : [[_ctx request] browserLanguages];
229 WOResponse_AddCString(_resp, "<img src=\"");
232 WOResourceManager *rm;
234 if ((rm = [[_ctx component] resourceManager]) == nil)
235 rm = [[_ctx application] resourceManager];
237 uFi = [rm urlForResourceNamed:uFi
239 [self->framework stringValueInComponent:sComponent]
241 request:[_ctx request]];
243 NSLog(@"%@: did not find resource %@", sComponent,
244 [self->filename stringValueInComponent:sComponent]);
247 [_resp appendContentHTMLAttributeValue:uFi];
250 [_resp appendContentHTMLAttributeValue:uUri];
252 WOResponse_AddChar(_resp, '"');
254 [self appendExtraAttributesToResponse:_resp inContext:_ctx];
255 WOResponse_AddCString(_resp, " />");
258 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
259 WOComponent *sComponent = [_ctx component];
261 NSString *targetView;
262 NSString *queryString = nil;
264 if ([[_ctx request] isFromClientComponent])
267 content = [self->string valueInContext:_ctx];
268 targetView = [self->target stringValueInComponent:sComponent];
270 WOResponse_AddCString(_response, "<a href=\"");
272 if ([self _appendHrefToResponse:_response inContext:_ctx]) {
273 queryString = [self queryStringForQueryDictionary:
274 [self->queryDictionary valueInComponent:sComponent]
275 andQueryParameters:self->queryParameters
279 if (self->fragmentIdentifier) {
280 [_response appendContentCharacter:'#'];
281 WOResponse_AddString(_response,
282 [self->fragmentIdentifier stringValueInComponent:sComponent]);
285 [_response appendContentCharacter:'?'];
286 WOResponse_AddString(_response, queryString);
288 [_response appendContentCharacter:'"'];
291 WOResponse_AddCString(_response, " target=\"");
292 WOResponse_AddString(_response, targetView);
293 [_response appendContentCharacter:'"'];
296 [self appendExtraAttributesToResponse:_response inContext:_ctx];
298 if (self->otherTagString) {
301 s = [self->otherTagString stringValueInComponent:[_ctx component]];
302 WOResponse_AddString(_response, s);
304 [_response appendContentCharacter:'>'];
307 [self->template appendToResponse:_response inContext:_ctx];
308 if (content) [_response appendContentHTMLString:content];
311 if ((self->src != nil) || (self->filename != nil))
312 [self _addImageToResponse:_response inContext:_ctx];
315 WOResponse_AddCString(_response, "</a>");
320 - (NSString *)associationDescription {
321 NSMutableString *str = [NSMutableString stringWithCapacity:256];
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];
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];
337 @end /* _WOComplexHyperlink */
339 @implementation _WOHrefHyperlink
341 static BOOL debugStaticLinks = NO;
344 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
346 debugStaticLinks = [ud boolForKey:@"WODebugStaticLinkProcessing"];
349 - (id)initWithName:(NSString *)_name
350 hyperlinkInfo:(WOHyperlinkInfo *)_info
351 template:(WOElement *)_t
353 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
354 self->href = _info->href;
360 [self->href release];
366 - (BOOL)_appendHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
371 base = [_ctx baseURL];
372 hrefValue = [self->href valueInContext:_ctx];
375 if (hrefValue == nil)
378 if ([hrefValue isKindOfClass:NSURLClass]) {
379 s = [hrefValue stringValueRelativeToURL:base];
382 s = [hrefValue stringValue];
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];
390 else if (base != nil) {
391 url = [NSURLClass URLWithString:s relativeToURL:base];
394 [self logWithFormat:@"WARNING: missing base URL in context ..."];
395 WOResponse_AddString(_r, s);
401 @"couldn't construct URL from 'href' string '%@' (base=%@)",
405 s = [url stringValueRelativeToURL:base];
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];
419 WOResponse_AddString(_r, s);
425 - (NSString *)associationDescription {
426 NSMutableString *str = [NSMutableString stringWithCapacity:256];
428 [str appendFormat:@" href=%@", self->href];
429 [str appendString:[super associationDescription]];
434 @end /* _WOHrefHyperlink */
436 @implementation _WOActionHyperlink
438 - (id)initWithName:(NSString *)_name
439 hyperlinkInfo:(WOHyperlinkInfo *)_info
440 template:(WOElement *)_t
442 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
443 self->action = _info->action;
449 [self->action release];
453 /* dynamic invocation */
455 - (id)invokeActionForRequest:(WORequest *)_request
456 inContext:(WOContext *)_ctx
458 if (self->disabled) {
459 if ([self->disabled boolValueInComponent:[_ctx component]])
463 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
464 /* link is not the active element */
465 return [self->template invokeActionForRequest:_request inContext:_ctx];
468 return [self executeAction:self->action inContext:_ctx];
471 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
472 inContext:(WOContext *)_ctx
474 WOResponse_AddString(_response, [_ctx componentActionURL]);
480 - (NSString *)associationDescription {
481 NSMutableString *str = [NSMutableString stringWithCapacity:256];
483 [str appendFormat:@" action=%@", self->action];
484 [str appendString:[super associationDescription]];
488 @end /* _WOActionHyperlink */
490 @implementation _WOPageHyperlink
492 - (id)initWithName:(NSString *)_name
493 hyperlinkInfo:(WOHyperlinkInfo *)_info
494 template:(WOElement *)_t
496 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
497 self->pageName = _info->pageName;
503 [self->pageName release];
509 - (id)invokeActionForRequest:(WORequest *)_request
510 inContext:(WOContext *)_ctx
515 if (self->disabled) {
516 if ([self->disabled boolValueInComponent:[_ctx component]])
520 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
521 /* link is not the active element */
522 return [self->template invokeActionForRequest:_request inContext:_ctx];
524 /* link is the active element */
526 name = [self->pageName stringValueInComponent:[_ctx component]];
527 page = [[_ctx application] pageWithName:name inContext:_ctx];
530 [[_ctx session] logWithFormat:
531 @"%@[0x%08X]: did not find page with name %@ !",
532 NSStringFromClass([self class]), self, name];
537 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
538 inContext:(WOContext *)_ctx
542 87% -componentActionURL
543 13% NSString dataUsingEncoding(appendContentString!)
544 TODO(prof): use addcstring
546 WOResponse_AddString(_response, [_ctx componentActionURL]);
552 - (NSString *)associationDescription {
553 NSMutableString *str = [NSMutableString stringWithCapacity:256];
555 [str appendFormat:@" pageName=%@", self->pageName];
556 [str appendString:[super associationDescription]];
560 @end /* _WOPageHyperlink */
562 @implementation _WODirectActionHyperlink
564 - (id)initWithName:(NSString *)_name
565 hyperlinkInfo:(WOHyperlinkInfo *)_info
566 template:(WOElement *)_t
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;
573 self->containsForm = NO; /* direct actions are never form stuff ... */
579 [self->actionClass release];
580 [self->directActionName release];
586 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
587 /* DA links can *never* take form values !!!! */
588 [self->template takeValuesFromRequest:_req inContext:_ctx];
591 - (id)invokeActionForRequest:(WORequest *)_request
592 inContext:(WOContext *)_ctx
594 /* DA links can *never* invoke an action !!!! */
595 return [self->template invokeActionForRequest:_request inContext:_ctx];
598 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
599 inContext:(WOContext *)_ctx
601 WOComponent *sComponent;
604 NSMutableDictionary *qd;
607 sComponent = [_ctx component];
608 daClass = [self->actionClass stringValueInComponent:sComponent];
609 daName = [self->directActionName stringValueInComponent:sComponent];
613 if (![daClass isEqualToString:@"DirectAction"])
614 daName = [NSString stringWithFormat:@"%@/%@", daClass, daName];
620 qd = [NSMutableDictionary dictionaryWithCapacity:16];
622 /* add query dictionary */
624 if (self->queryDictionary) {
625 if ((tmp = [self->queryDictionary valueInComponent:sComponent]))
626 [qd addEntriesFromDictionary:tmp];
629 /* add ?style parameters */
631 if (self->queryParameters) {
635 keys = [self->queryParameters keyEnumerator];
636 while ((key = [keys nextObject])) {
639 assoc = [self->queryParameters objectForKey:key];
640 value = [assoc stringValueInComponent:sComponent];
642 [qd setObject:(value ? value : @"") forKey:key];
648 if (self->sidInUrl) {
649 if ([_ctx hasSession]) {
653 [qd setObject:[sn sessionID] forKey:WORequestValueSessionID];
655 if (![sn isDistributionEnabled]) {
656 [qd setObject:[[WOApplication application] number]
657 forKey:WORequestValueInstance];
662 WOResponse_AddString(_response,
663 [_ctx directActionURLForActionNamed:daName
664 queryDictionary:qd]);
670 - (NSString *)associationDescription {
671 NSMutableString *str = [NSMutableString stringWithCapacity:256];
673 if (self->actionClass)
674 [str appendFormat:@" actionClass=%@", self->actionClass];
675 if (self->directActionName)
676 [str appendFormat:@" directAction=%@", self->directActionName];
678 [str appendString:[super associationDescription]];
682 @end /* _WODirectActionHyperlink */