2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "WOHyperlink.h"
26 [WOHTMLDynamicElement]
32 _WODirectActionHyperlink
35 @interface _WOComplexHyperlink : WOHyperlink
37 /* superclass of most hyperlink classes */
39 WOAssociation *fragmentIdentifier;
40 WOAssociation *string;
41 WOAssociation *target;
42 WOAssociation *disabled;
46 WOAssociation *queryDictionary;
47 NSDictionary *queryParameters; /* associations beginning with ? */
49 /* non WO, image stuff */
50 WOAssociation *filename; /* path relative to WebServerResources */
51 WOAssociation *framework;
52 WOAssociation *src; /* absolute URL */
53 WOAssociation *disabledFilename; /* icon for 'disabled' state */
56 - (NSString *)associationDescription;
60 @interface _WOHrefHyperlink : _WOComplexHyperlink
66 @interface _WOActionHyperlink : _WOComplexHyperlink
68 WOAssociation *action;
72 @interface _WOPageHyperlink : _WOComplexHyperlink
74 WOAssociation *pageName;
78 @interface _WODirectActionHyperlink : _WOComplexHyperlink
80 WOAssociation *actionClass;
81 WOAssociation *directActionName;
82 BOOL sidInUrl; /* include session-id in wa URL ? */
86 #include "WOElement+private.h"
87 #include "WOHyperlinkInfo.h"
88 #include "WOCompoundElement.h"
89 #include <NGObjWeb/WOApplication.h>
90 #include <NGObjWeb/WOResourceManager.h>
91 #include <NGExtensions/NSString+Ext.h>
94 static Class NSURLClass = Nil;
96 @implementation _WOComplexHyperlink
99 return [super version] /* v4 */;
102 NSAssert2([super version] == 4,
103 @"invalid superclass (%@) version %i !",
104 NSStringFromClass([self superclass]), [super version]);
105 if (NSURLClass == Nil)
106 NSURLClass = [NSURL class];
109 - (id)initWithName:(NSString *)_name
110 hyperlinkInfo:(WOHyperlinkInfo *)_info
111 template:(WOElement *)_t
113 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
114 self->template = [_t retain];
116 self->fragmentIdentifier = _info->fragmentIdentifier;
117 self->string = _info->string;
118 self->target = _info->target;
119 self->disabled = _info->disabled;
120 self->queryDictionary = _info->queryDictionary;
121 self->queryParameters = _info->queryParameters;
124 self->filename = _info->filename;
125 self->framework = _info->framework;
126 self->src = _info->src;
127 self->disabledFilename = _info->disabledFilename;
129 self->containsForm = self->queryParameters ? YES : NO;
135 [self->template release];
136 [self->queryDictionary release];
137 [self->queryParameters release];
138 [self->disabledFilename release];
139 [self->filename release];
140 [self->framework release];
142 [self->fragmentIdentifier release];
143 [self->string release];
144 [self->target release];
145 [self->disabled release];
152 return self->template;
155 /* handle requests */
157 - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
158 /* links can take form values !!!! (for query-parameters) */
160 if (self->queryParameters != nil) {
161 /* apply values to ?style parameters */
162 WOComponent *sComponent = [_ctx component];
166 keys = [self->queryParameters keyEnumerator];
167 while ((key = [keys nextObject]) != nil) {
170 assoc = [self->queryParameters objectForKey:key];
172 if ([assoc isValueSettable]) {
173 value = [_rq formValueForKey:key];
174 [assoc setValue:value inComponent:sComponent];
179 [self->template takeValuesFromRequest:_rq inContext:_ctx];
182 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
183 if (self->disabled != nil) {
184 if ([self->disabled boolValueInComponent:[_ctx component]])
188 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
189 /* link is not the active element */
190 return [self->template invokeActionForRequest:_rq inContext:_ctx];
193 [[_ctx session] logWithFormat:@"%@[0x%08X]: no action/page set !",
194 NSStringFromClass([self class]), self];
198 - (BOOL)_appendHrefToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
199 [self subclassResponsibility:_cmd];
203 - (void)_addImageToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
204 WOComponent *sComponent = [_ctx component];
209 uUri = [[self->src valueInContext:_ctx] stringValue];
211 if ([self->disabled boolValueInComponent:sComponent]) {
212 uFi = [self->disabledFilename stringValueInComponent:sComponent];
214 uFi = [self->filename stringValueInComponent:sComponent];
217 uFi = [self->filename stringValueInComponent:sComponent];
219 if (!((uFi != nil) || (uUri != nil)))
222 languages = [_ctx resourceLookupLanguages];
224 WOResponse_AddCString(_resp, "<img src=\"");
227 WOResourceManager *rm;
229 if ((rm = [[_ctx component] resourceManager]) == nil)
230 rm = [[_ctx application] resourceManager];
232 uFi = [rm urlForResourceNamed:uFi
234 [self->framework stringValueInComponent:sComponent]
236 request:[_ctx request]];
238 NSLog(@"%@: did not find resource %@", sComponent,
239 [self->filename stringValueInComponent:sComponent]);
242 [_resp appendContentHTMLAttributeValue:uFi];
245 [_resp appendContentHTMLAttributeValue:uUri];
247 WOResponse_AddChar(_resp, '"');
249 [self appendExtraAttributesToResponse:_resp inContext:_ctx];
251 WOResponse_AddEmptyCloseParens(_resp, _ctx);
254 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
255 WOComponent *sComponent = [_ctx component];
259 if ([[_ctx request] isFromClientComponent])
262 content = [self->string valueInContext:_ctx];
263 doNotDisplay = [self->disabled boolValueInComponent:sComponent];
266 NSString *targetView;
267 NSString *queryString = nil;
269 targetView = [self->target stringValueInComponent:sComponent];
271 WOResponse_AddCString(_response, "<a href=\"");
273 if ([self _appendHrefToResponse:_response inContext:_ctx]) {
274 queryString = [self queryStringForQueryDictionary:
275 [self->queryDictionary valueInComponent:sComponent]
276 andQueryParameters:self->queryParameters
280 if (self->fragmentIdentifier) {
281 [_response appendContentCharacter:'#'];
282 WOResponse_AddString(_response,
283 [self->fragmentIdentifier stringValueInComponent:sComponent]);
286 [_response appendContentCharacter:'?'];
287 WOResponse_AddString(_response, queryString);
289 [_response appendContentCharacter:'"'];
292 WOResponse_AddCString(_response, " target=\"");
293 WOResponse_AddString(_response, targetView);
294 [_response appendContentCharacter:'"'];
297 [self appendExtraAttributesToResponse:_response inContext:_ctx];
299 if (self->otherTagString) {
300 WOResponse_AddChar(_response, ' ');
301 WOResponse_AddString(_response,
302 [self->otherTagString stringValueInComponent:
305 [_response appendContentCharacter:'>'];
309 [self->template appendToResponse:_response inContext:_ctx];
310 if (content) [_response appendContentHTMLString:content];
313 if ((self->src != nil) || (self->filename != nil))
314 [self _addImageToResponse:_response inContext:_ctx];
318 WOResponse_AddCString(_response, "</a>");
324 - (NSString *)associationDescription {
325 NSMutableString *str = [NSMutableString stringWithCapacity:256];
327 if (self->fragmentIdentifier)
328 [str appendFormat:@" fragment=%@", self->fragmentIdentifier];
329 if (self->string) [str appendFormat:@" string=%@", self->string];
330 if (self->target) [str appendFormat:@" target=%@", self->target];
331 if (self->disabled) [str appendFormat:@" disabled=%@", self->disabled];
334 if (self->filename) [str appendFormat:@" filename=%@", self->filename];
335 if (self->framework) [str appendFormat:@" framework=%@", self->framework];
336 if (self->src) [str appendFormat:@" src=%@", self->src];
341 @end /* _WOComplexHyperlink */
344 @implementation _WOHrefHyperlink
346 static BOOL debugStaticLinks = NO;
349 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
351 debugStaticLinks = [ud boolForKey:@"WODebugStaticLinkProcessing"];
354 - (id)initWithName:(NSString *)_name
355 hyperlinkInfo:(WOHyperlinkInfo *)_info
356 template:(WOElement *)_t
358 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
359 self->href = _info->href;
365 [self->href release];
371 - (BOOL)shouldRewriteURLString:(NSString *)_s inContext:(WOContext *)_ctx {
372 // TODO: we need a binding to disable rewriting!
375 r = [_s rangeOfString:@":"];
379 /* only rewrite HTTP URLs */
380 return [_s hasPrefix:@"http"];
383 - (BOOL)_appendHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
388 base = [_ctx baseURL];
389 hrefValue = [self->href valueInContext:_ctx];
392 if (hrefValue == nil)
395 if ((*(Class *)hrefValue == NSURLClass) ||
396 [hrefValue isKindOfClass:NSURLClass]) {
397 s = [hrefValue stringValueRelativeToURL:base];
400 /* given HREF is a string */
401 s = [hrefValue stringValue];
403 /* we do not want to rewrite stuff like mailto: or javascript: URLs */
404 if ([self shouldRewriteURLString:s inContext:_ctx]) {
405 if ([s isAbsoluteURL]) {
406 // TODO: why are we doing this? we could just pass through the string?
407 // => probably to generate relative links
408 url = [NSURLClass URLWithString:s];
410 else if (base != nil) {
411 /* avoid creating a new URL for ".", just return the base */
412 url = [s isEqualToString:@"."]
414 : [NSURLClass URLWithString:s relativeToURL:base];
417 [self logWithFormat:@"WARNING: missing base URL in context ..."];
418 WOResponse_AddString(_r, s);
424 @"could not construct URL from 'href' string '%@' (base=%@)",
429 s = [url stringValueRelativeToURL:base];
435 if (debugStaticLinks) {
436 [self logWithFormat:@"static links based on 'href': '%@'", hrefValue];
437 [self logWithFormat:@" base %@", base];
438 [self logWithFormat:@" base-abs %@", [base absoluteString]];
439 [self logWithFormat:@" url %@", url];
441 [self logWithFormat:@" url-abs %@", [url absoluteString]];
443 [self logWithFormat:@" href %@", hrefValue];
444 [self logWithFormat:@" string %@", s];
447 WOResponse_AddString(_r, s);
453 - (NSString *)associationDescription {
454 NSMutableString *str = [NSMutableString stringWithCapacity:256];
456 [str appendFormat:@" href=%@", self->href];
457 [str appendString:[super associationDescription]];
462 @end /* _WOHrefHyperlink */
465 @implementation _WOActionHyperlink
467 - (id)initWithName:(NSString *)_name
468 hyperlinkInfo:(WOHyperlinkInfo *)_info
469 template:(WOElement *)_t
471 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
472 self->action = _info->action;
478 [self->action release];
482 /* dynamic invocation */
484 - (id)invokeActionForRequest:(WORequest *)_rq
485 inContext:(WOContext *)_ctx
487 if (self->disabled) {
488 if ([self->disabled boolValueInComponent:[_ctx component]])
492 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
493 /* link is not the active element */
494 return [self->template invokeActionForRequest:_rq inContext:_ctx];
497 return [self executeAction:self->action inContext:_ctx];
500 - (BOOL)_appendHrefToResponse:(WOResponse *)_response
501 inContext:(WOContext *)_ctx
503 WOResponse_AddString(_response, [_ctx componentActionURL]);
509 - (NSString *)associationDescription {
510 NSMutableString *str = [NSMutableString stringWithCapacity:256];
512 [str appendFormat:@" action=%@", self->action];
513 [str appendString:[super associationDescription]];
517 @end /* _WOActionHyperlink */
520 @implementation _WOPageHyperlink
522 - (id)initWithName:(NSString *)_name
523 hyperlinkInfo:(WOHyperlinkInfo *)_info
524 template:(WOElement *)_t
526 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
527 self->pageName = _info->pageName;
533 [self->pageName release];
539 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
543 if (self->disabled) {
544 if ([self->disabled boolValueInComponent:[_ctx component]])
548 if (![[_ctx elementID] isEqualToString:[_ctx senderID]])
549 /* link is not the active element */
550 return [self->template invokeActionForRequest:_rq inContext:_ctx];
552 /* link is the active element */
554 name = [self->pageName stringValueInComponent:[_ctx component]];
555 page = [[_ctx application] pageWithName:name inContext:_ctx];
558 [[_ctx session] logWithFormat:
559 @"%@[0x%08X]: did not find page with name %@ !",
560 NSStringFromClass([self class]), self, name];
565 /* generate response */
567 - (BOOL)_appendHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
570 87% -componentActionURL
571 13% NSString dataUsingEncoding(appendContentString!)
572 TODO(prof): use addcstring
574 WOResponse_AddString(_r, [_ctx componentActionURL]);
580 - (NSString *)associationDescription {
581 NSMutableString *str = [NSMutableString stringWithCapacity:256];
583 [str appendFormat:@" pageName=%@", self->pageName];
584 [str appendString:[super associationDescription]];
588 @end /* _WOPageHyperlink */
591 @implementation _WODirectActionHyperlink
593 - (id)initWithName:(NSString *)_name
594 hyperlinkInfo:(WOHyperlinkInfo *)_info
595 template:(WOElement *)_t
597 if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
598 self->actionClass = _info->actionClass;
599 self->directActionName = _info->directActionName;
600 self->sidInUrl = _info->sidInUrl;
602 self->containsForm = NO; /* direct actions are never form stuff ... */
608 [self->actionClass release];
609 [self->directActionName release];
613 /* handle requests */
615 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
616 /* DA links can *never* take form values !!!! */
617 [self->template takeValuesFromRequest:_req inContext:_ctx];
620 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
621 /* DA links can *never* invoke an action !!!! */
622 return [self->template invokeActionForRequest:_rq inContext:_ctx];
625 /* generate response */
627 - (BOOL)_appendHrefToResponse:(WOResponse *)_r
628 inContext:(WOContext *)_ctx
630 WOComponent *sComponent;
633 NSMutableDictionary *qd;
636 sComponent = [_ctx component];
637 daClass = [self->actionClass stringValueInComponent:sComponent];
638 daName = [self->directActionName stringValueInComponent:sComponent];
642 if (![daClass isEqualToString:@"DirectAction"])
643 daName = [NSString stringWithFormat:@"%@/%@", daClass, daName];
649 qd = [NSMutableDictionary dictionaryWithCapacity:16];
651 /* add query dictionary */
653 if (self->queryDictionary) {
654 if ((tmp = [self->queryDictionary valueInComponent:sComponent]))
655 [qd addEntriesFromDictionary:tmp];
658 /* add ?style parameters */
660 if (self->queryParameters) {
664 keys = [self->queryParameters keyEnumerator];
665 while ((key = [keys nextObject])) {
668 assoc = [self->queryParameters objectForKey:key];
669 value = [assoc stringValueInComponent:sComponent];
671 [qd setObject:(value ? value : @"") forKey:key];
677 if (self->sidInUrl) {
678 if ([_ctx hasSession]) {
682 [qd setObject:[sn sessionID] forKey:WORequestValueSessionID];
684 if (![sn isDistributionEnabled]) {
685 [qd setObject:[[WOApplication application] number]
686 forKey:WORequestValueInstance];
691 WOResponse_AddString(_r,
692 [_ctx directActionURLForActionNamed:daName
693 queryDictionary:qd]);
699 - (NSString *)associationDescription {
700 NSMutableString *str = [NSMutableString stringWithCapacity:256];
702 if (self->actionClass)
703 [str appendFormat:@" actionClass=%@", self->actionClass];
704 if (self->directActionName)
705 [str appendFormat:@" directAction=%@", self->directActionName];
707 [str appendString:[super associationDescription]];
711 @end /* _WODirectActionHyperlink */