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
22 #include <NGObjWeb/WOContext.h>
23 #include "NSObject+WO.h"
24 #include "WOComponent+private.h"
25 #include "WOContext+private.h"
26 #include "WOApplication+private.h"
27 #include <NGObjWeb/WOApplication.h>
28 #include <NGObjWeb/WORequest.h>
29 #include <NGObjWeb/WOResponse.h>
30 #include <NGObjWeb/WOSession.h>
31 #import <EOControl/EONull.h>
32 #include "WOElementID.h"
37 @interface WOContext(Privates5)
38 - (NSArray *)_componentStack;
41 @interface WOComponent(Cursors)
42 - (void)pushCursor:(id)_obj;
47 static Class WOAppClass = Nil;
49 @implementation WOContext
55 static Class MutableStrClass = Nil;
56 static int contextCount = 0;
57 static int logComponents = -1;
58 static int relativeURLs = -1;
59 static BOOL debugOn = NO;
60 static int debugCursor = -1;
61 static BOOL debugComponentAwake = NO;
62 static BOOL testNSURLs = NO;
63 static BOOL newCURLStyle = NO;
64 static NSString *WOApplicationSuffix = nil;
67 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
68 static BOOL didInitialize = NO;
69 if (didInitialize) return;
71 if (WOAppClass == Nil)
72 WOAppClass = [WOApplication class];
73 if (MutableStrClass == Nil)
74 MutableStrClass = [NSMutableString class];
78 logComponents = [[ud objectForKey:@"WOLogComponents"] boolValue] ? 1 : 0;
79 relativeURLs = [[ud objectForKey:@"WOUseRelativeURLs"] boolValue]? 1 : 0;
80 debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0;
81 debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
82 WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
85 + (id)contextWithRequest:(WORequest *)_request {
86 return [[(WOContext *)[self alloc] initWithRequest:_request] autorelease];
89 - (id)initWithRequest:(WORequest *)_request {
90 if ((self = [super init])) {
91 unsigned char buf[24];
94 sprintf(buf, "%03x%08x%08x", ++contextCount, (int)time(NULL), (int)self);
95 self->ctxId = [[NSString alloc] initWithCString:buf];
97 self->elementID = [[WOElementID alloc] init];
98 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
100 self->request = [_request retain];
101 self->response = [[WOResponse responseWithRequest:_request] retain];
107 return [[[self alloc] init] autorelease];
110 return [self initWithRequest:nil];
115 - (void)_addAwakeComponent:(WOComponent *)_component {
116 if (_component == nil)
119 if ([self->awakeComponents containsObject:_component])
122 /* wake up component */
123 if (debugComponentAwake)
124 [self logWithFormat:@"mark component awake: %@", _component];
126 [self->awakeComponents addObject:_component];
129 - (void)_awakeComponent:(WOComponent *)_component {
130 if (_component == nil)
133 if ([self->awakeComponents containsObject:_component])
136 /* wake up component */
137 if (debugComponentAwake)
138 [self logWithFormat:@"awake component: %@", _component];
140 [_component _awakeWithContext:self];
142 [self _addAwakeComponent:_component];
144 if (debugComponentAwake)
145 [self logWithFormat:@"woke up component: %@", _component];
148 - (void)sleepComponents {
150 WOComponent *component;
151 BOOL sendSleepToPage;
153 if (debugComponentAwake) {
154 [self logWithFormat:@"sleep %d components ...",
155 [self->awakeComponents count]];
158 sendSleepToPage = YES;
159 e = [self->awakeComponents objectEnumerator];
160 while ((component = [e nextObject])) {
161 if (debugComponentAwake)
162 [self logWithFormat:@" sleep component: %@", component];
163 [component _sleepWithContext:self];
164 if (component == self->page) sendSleepToPage = NO;
166 if (sendSleepToPage && (self->page != nil)) {
167 if (debugComponentAwake)
168 [self logWithFormat:@" sleep page: %@", self->page];
169 [self->page _sleepWithContext:self];
172 if (debugComponentAwake) {
173 [self logWithFormat:@"done sleep %d components.",
174 [self->awakeComponents count]];
176 [self->awakeComponents removeAllObjects];
179 #if WITH_DEALLOC_OBSERVERS
180 - (void)addDeallocObserver:(id)_observer {
181 if (_observer == NULL) return;
184 if (self->deallocObservers == NULL) {
185 self->deallocObservers = calloc(8, sizeof(id));
186 self->deallocObserverCount = 0;
187 self->deallocObserverCapacity = 8;
191 if (self->deallocObserverCapacity == self->deallocObserverCount) {
192 /* need to increase array */
195 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
196 memcpy(newa, self->deallocObservers,
197 sizeof(id) * self->deallocObserverCount);
198 free(self->deallocObservers);
199 self->deallocObservers = newa;
200 self->deallocObserverCapacity *= 2;
204 self->deallocObservers[self->deallocObserverCount] = _observer;
205 self->deallocObserverCount++;
207 - (void)removeDeallocObserver:(id)_observer {
208 /* the observer currently will only grow (this should be OK for WOContext) */
210 if (_observer == NULL) return;
212 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
213 if ((self->deallocObservers[i]) == _observer)
214 self->deallocObservers[i] = NULL;
220 [self sleepComponents];
222 #if WITH_DEALLOC_OBSERVERS
223 if (self->deallocObservers) {
227 printf("%s: dealloc observer capacity: %i\n",
228 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
231 /* GC!! process in reverse order ... */
232 for (i = self->deallocObserverCount - 1; i >= 0; i++)
233 [self->deallocObservers[i] _objectWillDealloc:self];
235 free(self->deallocObservers);
236 self->deallocObservers = NULL;
240 [self->activeUser release];
241 [self->rootURL release];
242 [self->objectPermissionCache release];
243 [self->traversalStack release];
244 [self->clientObject release];
245 [self->objectDispatcher release];
246 [self->soRequestType release];
247 [self->pathInfo release];
249 [[NSNotificationCenter defaultCenter]
250 postNotificationName:@"WOContextWillDeallocate"
253 { /* release component stack */
255 for (i = (self->componentStackCount - 1); i >= 0; i--) {
256 [self->componentStack[i] release]; self->componentStack[i] = nil;
257 [self->contentStack[i] release]; self->contentStack[i] = nil;
261 [self->urlPrefix release];
262 [self->elementID release];
263 [self->reqElementID release];
264 [self->activeFormElement release];
265 [self->page release];
266 [self->awakeComponents release];
267 [self->appURL release];
268 [self->baseURL release];
269 [self->session release];
270 [self->variables release];
271 [self->request release];
272 [self->response release];
273 [self->ctxId release];
277 - (void)setSession:(WOSession *)_session {
278 ASSIGN(self->session, _session);
281 - (WOSession *)session {
282 // in WO4 -session creates a new session if none is associated
284 if (self->session == nil) {
285 [[self application] _initializeSessionInContext:self];
287 if (self->session == nil)
288 [self logWithFormat:@"%s: missing session for context ..",
289 __PRETTY_FUNCTION__];
292 return self->session;
295 - (NSString *)contextID {
296 NSAssert(self->ctxId, @"context without id !");
298 // in WO4 -contextID returns nil if there is no associated session
299 return self->session ? self->ctxId : nil;
302 IMHO the above isn't true, otherwise session cannot be automagically
305 TODO: well, we might want to generate component URLs which work without
306 a session - at least in theory the ID tree should be stable even
307 without a session (and if proper uids are used for dynamic content).
308 eg this would be quite useful for SOPE.
314 - (WORequest *)request {
315 return self->request;
317 - (WOResponse *)response {
318 return self->response;
322 return (self->session != nil) ? YES : NO;
325 - (BOOL)savePageRequired {
326 return self->savePageRequired;
331 - (void)pushCursor:(id)_obj {
332 if (debugCursor == -1) {
333 debugCursor = [[NSUserDefaults standardUserDefaults]
334 boolForKey:@"WODebugCursor"]
338 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
339 [[self component] pushCursor:_obj];
343 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
344 return [[self component] popCursor];
348 return [(id <WOPageGenerationContext>)[self component] cursor];
353 - (WOComponent *)component {
354 return (self->componentStackCount > 0)
355 ? self->componentStack[self->componentStackCount - 1]
359 - (void)setPage:(WOComponent *)_page {
360 [_page ensureAwakeInContext:self];
361 ASSIGN(self->page, _page);
363 - (WOComponent *)page {
367 void WOContext_enterComponent
368 (WOContext *self, WOComponent *_component, WOElement *_content)
370 WOComponent *parent = nil;
372 NSCAssert(_component, @"missing component to enter ...");
376 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
377 [_component name], _content];
380 parent = self->componentStackCount > 0
381 ? self->componentStack[self->componentStackCount - 1]
384 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
385 @"exceeded maximum component nesting depth (%i):\n%@",
386 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
387 [self _componentStack]);
388 self->componentStack[(int)self->componentStackCount] = [_component retain];
389 self->contentStack[(int)self->componentStackCount] = [_content retain];
390 self->componentStackCount++;
392 [self _awakeComponent:_component];
395 if ([_component synchronizesVariablesWithBindings])
396 WOComponent_syncFromParent(_component, parent);
399 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
400 WOComponent *parent = nil;
404 parent = (self->componentStackCount > 1)
405 ? self->componentStack[self->componentStackCount - 2]
409 if ([_component synchronizesVariablesWithBindings])
410 WOComponent_syncToParent(_component, parent);
413 PROFILE_CHECKPOINT("after sync");
415 /* remove last object */
416 self->componentStackCount--;
417 NSCAssert(self->componentStackCount >= 0,
418 @"tried to pop component from empty component stack !");
419 [self->componentStack[(int)self->componentStackCount] release];
420 self->componentStack[(int)self->componentStackCount] = nil;
421 [self->contentStack[(int)self->componentStackCount] release];
422 self->contentStack[(int)self->componentStackCount] = nil;
425 [self->application logWithFormat:@"left component %@.", [_component name]];
430 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
431 WOContext_enterComponent(self, _comp, _content);
433 - (void)leaveComponent:(WOComponent *)_component {
435 WOContext_leaveComponent(self, _component);
439 - (WOComponent *)parentComponent {
440 return (self->componentStackCount > 1)
441 ? self->componentStack[(int)self->componentStackCount - 2]
445 - (WODynamicElement *)componentContent {
446 return (self->componentStackCount > 0)
447 ? self->contentStack[(int)self->componentStackCount - 1]
451 - (unsigned)componentStackCount {
452 return self->componentStackCount;
454 - (NSArray *)_componentStack {
455 return [NSArray arrayWithObjects:self->componentStack
456 count:self->componentStackCount];
461 - (NSURL *)serverURL {
467 if ((rq = [self request]) == nil) {
468 [self logWithFormat:@"missing request in -baseURL call .."];
472 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
473 if ((host = [rq headerForKey:@"host"]))
474 serverURL = [@"http://" stringByAppendingString:host];
477 // TODO: fix that (host is also broken for example with SOUP)
478 /* sometimes the port is broken in the server URL ... */
479 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
480 if ((host = [rq headerForKey:@"host"])) {
482 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
483 serverURL = [scheme stringByAppendingString:host];
488 if ([serverURL length] == 0) {
490 @"ERROR: could not find x-webobjects-server-url header !"];
494 if ((url = [NSURL URLWithString:serverURL]) == nil) {
495 [self logWithFormat:@"could not construct NSURL from string '%@'",
507 return self->baseURL;
509 if ((rq = [self request]) == nil) {
510 [self logWithFormat:@"missing request in -baseURL call .."];
514 serverURL = [self serverURL];
516 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
518 if (self->baseURL == nil) {
520 @"could not construct NSURL for uri '%@' and base '%@' ...",
521 [rq uri], serverURL];
523 return self->baseURL;
526 - (NSURL *)applicationURL {
529 if (self->appURL != nil)
532 // TODO: we should ensure that the suffix (.woa) is in the URL
534 s = [self->request adaptorPrefix];
535 if ([s length] > 0) {
536 s = [[[s stringByAppendingString:@"/"]
537 stringByAppendingString:[self->request applicationName]]
538 stringByAppendingString:@"/"];
541 s = [[self->request applicationName] stringByAppendingString:@"/"];
544 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
547 - (NSURL *)urlForKey:(NSString *)_key {
548 _key = [_key stringByAppendingString:@"/"];
549 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
554 - (void)setInForm:(BOOL)_form {
555 self->inForm = _form;
561 - (void)addActiveFormElement:(WOElement *)_formElement {
562 if (self->activeFormElement) {
563 [[self component] debugWithFormat:@"active form element already set !"];
567 ASSIGN(self->activeFormElement, _formElement);
568 [self setRequestSenderID:[self elementID]];
570 - (WOElement *)activeFormElement {
571 return self->activeFormElement;
574 /* context variables (transient) */
576 - (void)setObject:(id)_obj forKey:(NSString *)_key {
577 if (self->variables == nil) {
579 [[NSMutableDictionary allocWithZone:[self zone]]
580 initWithCapacity:16];
584 [self->variables setObject:_obj forKey:_key];
586 [self->variables removeObjectForKey:_key];
588 - (id)objectForKey:(NSString *)_key {
589 return [self->variables objectForKey:_key];
591 - (void)removeObjectForKey:(NSString *)_key {
592 [self->variables removeObjectForKey:_key];
595 - (NSDictionary *)variableDictionary {
596 return self->variables;
599 - (void)takeValue:(id)_value forKey:(NSString *)_key {
600 if (WOSetKVCValueUsingMethod(self, _key, _value))
603 else if (WOGetKVCGetMethod(self, _key) == NULL) {
605 _value = [EONull null];
607 if (self->variables == nil) {
609 [[NSMutableDictionary allocWithZone:[self zone]]
610 initWithCapacity:16];
612 [self->variables setObject:_value forKey:_key];
616 // only a 'get' method is defined for _key !
617 [self handleTakeValue:_value forUnboundKey:_key];
620 - (id)valueForKey:(NSString *)_key {
623 if ((value = WOGetKVCValueUsingMethod(self, _key)))
625 value = [self->variables objectForKey:_key];
631 - (id)copyWithZone:(NSZone *)_zone {
632 return [self retain];
637 - (NSString *)description {
639 WOApplication *app = [self application];
641 if ([self hasSession])
642 sid = [[self session] sessionID];
644 return [NSString stringWithFormat:
645 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
646 (unsigned)self, NSStringFromClass([self class]),
656 @implementation WOContext(ElementIDs)
658 - (NSString *)elementID {
659 return [self->elementID elementID];
661 - (void)appendElementIDComponent:(NSString *)_eid {
662 [self->elementID appendElementIDComponent:_eid];
664 - (void)appendZeroElementIDComponent {
665 [self->elementID appendZeroElementIDComponent];
667 - (void)deleteAllElementIDComponents {
668 [self->elementID deleteAllElementIDComponents];
670 - (void)deleteLastElementIDComponent {
671 [self->elementID deleteLastElementIDComponent];
673 - (void)incrementLastElementIDComponent {
674 [self->elementID incrementLastElementIDComponent];
676 - (void)appendIntElementIDComponent:(int)_eid {
677 [self->elementID appendIntElementIDComponent:_eid];
680 /* the following can be later moved to WOElementID */
682 - (id)currentElementID {
683 return [self->reqElementID currentElementID];
685 - (id)consumeElementID {
686 return [self->reqElementID consumeElementID];
689 @end /* WOContext(ElementIDs) */
691 @implementation WOContext(URLs)
693 - (void)_generateCompleteURLs {
694 /* described in Apple TIL article 70101 */
697 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
703 qs = [MutableStrClass stringWithCapacity:256];
704 keys = [_queryDict keyEnumerator];
705 for (isFirst = YES; (key = [keys nextObject]); ) {
711 [qs appendString:self->qpJoin];
713 value = [[_queryDict objectForKey:key] stringValue];
715 key = [key stringByEscapingURL];
716 value = [value stringByEscapingURL];
718 [qs appendString:key];
720 [qs appendString:@"="];
721 [qs appendString:value];
728 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
729 queryDictionary:(NSDictionary *)_queryDict
731 NSMutableString *url;
734 url = [MutableStrClass stringWithCapacity:256];
737 [url appendString:@"/"];
739 [url appendString:_actionName];
741 /* add query parameters */
743 qs = [self queryStringFromDictionary:_queryDict];
745 return [self urlWithRequestHandlerKey:
746 [WOAppClass directActionRequestHandlerKey]
747 path:url queryString:qs];
750 - (NSString *)componentActionURL {
751 // TODO: add a -cComponentActionURL
752 // (without NSString for use with appendContentCString:)
754 // 26% -urlWithRequestHandler...
755 // 21% -elementID (was 40% !! :-)
756 // ~20% mutable string ops
761 self->savePageRequired = YES;
762 qs = [MutableStrClass stringWithCapacity:64];
764 [qs appendString:WORequestValueSenderID];
765 [qs appendString:@"="];
766 [qs appendString:[self elementID]];
767 [qs appendString:self->qpJoin];
768 [qs appendString:WORequestValueSessionID];
769 [qs appendString:@"="];
770 [qs appendString:[[self session] sessionID]];
771 [qs appendString:self->qpJoin];
772 [qs appendString:WORequestValueContextID];
773 [qs appendString:@"="];
774 [qs appendString:[self contextID]];
776 p = [[self page] componentActionURLForContext:self];
779 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
782 return [self urlWithRequestHandlerKey:
783 [WOAppClass componentRequestHandlerKey]
788 /* old style URLs ... */
789 static NSMutableString *url = nil; // THREAD
790 static IMP addStr = NULL;
793 self->savePageRequired = YES;
795 url = [[MutableStrClass alloc] initWithCapacity:256];
796 addStr = [url methodForSelector:@selector(appendString:)];
797 addStr(url, @selector(appendString:), @"/");
800 [url setString:@"/"];
803 Note: component actions *always* require sessions to be able to locate
804 the request component !
806 addStr(url, @selector(appendString:), [[self session] sessionID]);
807 addStr(url, @selector(appendString:), @"/");
808 addStr(url, @selector(appendString:), [self->elementID elementID]);
810 s = [self urlWithRequestHandlerKey:
811 [WOAppClass componentRequestHandlerKey]
812 path:url queryString:nil];
817 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
818 path:(NSString *)_path
819 queryString:(NSString *)_query
821 if (testNSURLs) { /* use NSURLs for processing */
824 if ([_path hasPrefix:@"/"]) {
826 [self logWithFormat:@"WARNING: got absolute path '%@'", _path];
828 _path = [_path substringFromIndex:1];
831 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
832 rqUrl = [self urlForKey:_key];
834 if ([_query length] > 0) {
837 s = [_path mutableCopy];
838 [s appendString:@"?"];
839 [s appendString:_query];
840 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
844 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
846 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
848 return [rqUrl stringValueRelativeToURL:[self baseURL]];
851 NSMutableString *url;
855 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
857 url = [MutableStrClass stringWithCapacity:256];
858 addStr = [url methodForSelector:@selector(appendString:)];
861 if (self->urlPrefix == nil) {
863 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
864 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
865 tmp = [tmp substringToIndex:([tmp length] - 2)];
866 addStr(url, @selector(appendString:), tmp);
868 else if ((tmp = [self->request headerForKey:@"host"])) {
869 addStr(url, @selector(appendString:), @"http://");
870 addStr(url, @selector(appendString:), tmp);
874 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
875 addStr(url, @selector(appendString:), @"/");
876 tmp = [[self request] applicationName];
877 if ([tmp length] == 0)
878 tmp = [(WOApplication *)[self application] name];
879 if ([tmp length] > 0) {
880 addStr(url, @selector(appendString:), tmp);
881 if (WOApplicationSuffix)
882 addStr(url, @selector(appendString:), WOApplicationSuffix);
883 addStr(url, @selector(appendString:), @"/");
887 self->urlPrefix = [url copy];
888 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
891 /* prefix is cached :-) */
892 addStr(url, @selector(appendString:), self->urlPrefix);
896 addStr(url, @selector(appendString:), _key);
898 addStr(url, @selector(appendString:), _path);
899 if ([_query length] > 0) {
900 addStr(url, @selector(appendString:), @"?");
901 addStr(url, @selector(appendString:), _query);
906 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
907 path:(NSString *)_path queryString:(NSString *)_query
908 isSecure:(BOOL)_isSecure port:(int)_port
910 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
911 [url appendString:_isSecure ? @"https://" : @"http://"];
912 [url appendString:[[self request] headerForKey:@"host"]];
914 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
915 [url appendFormat:@":%i", _port];
917 [url appendString:[self urlWithRequestHandlerKey:_key
919 queryString:_query]];
923 - (void)setRequestSenderID:(NSString *)_rid {
926 eid = [[WOElementID alloc] initWithString:_rid];
927 [self->reqElementID release];
928 self->reqElementID = eid;
930 - (NSString *)senderID {
932 return [self->reqElementID elementID];
934 NSMutableString *eid;
938 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
939 addStr = [eid methodForSelector:@selector(appendString:)];
940 for (i = 0; i < self->reqElementIdCount; i++) {
941 if (i != 0) addStr(eid, @selector(appendString:), @".");
942 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
948 @end /* WOContext(URLs) */
950 @implementation WOContext(DeprecatedMethodsInWO4)
952 - (WOApplication *)application {
953 if (self->application == nil)
954 self->application = [WOAppClass application];
956 if (self->application == nil)
957 NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self);
959 return self->application;
962 - (void)setDistributionEnabled:(BOOL)_flag {
964 [[self session] setDistributionEnabled:_flag];
966 - (BOOL)isDistributionEnabled {
968 return [[self session] isDistributionEnabled];
972 return [self componentActionURL];
975 - (NSString *)urlSessionPrefix {
976 NSMutableString *url;
979 url = [MutableStrClass stringWithCapacity:128];
981 [url appendString:[[self request] adaptorPrefix]];
982 [url appendString:@"/"];
983 tmp = [[self request] applicationName];
985 tmp ? tmp : [(WOApplication *)[self application] name]];
988 if ([url length] == 0) {
989 NSLog(@"WARNING(%s): could not determine session URL prefix !",
990 __PRETTY_FUNCTION__);
997 @end /* WOContext(DeprecatedMethodsInWO4) */
999 @implementation WOComponent(Cursors)
1001 - (void)pushCursor:(id)_obj {
1002 NSMutableArray *ctxStack;
1005 [self logWithFormat:@"enter cursor: %@", _obj];
1007 if ((ctxStack = self->cycleContext) == nil)
1008 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1010 /* add to cursor stack */
1011 [ctxStack addObject:(_obj ? _obj : [NSNull null])];
1013 /* set active cursor */
1014 if (![_obj isNotNull]) _obj = nil;
1015 [self setObject:_obj forKey:@"_"];
1019 NSMutableArray *ctxStack;
1022 /* retrieve last context */
1023 old = [[self objectForKey:@"_"] retain];
1024 [self setObject:nil forKey:@"_"];
1026 /* restore old ctx */
1027 if ((ctxStack = self->cycleContext) != nil) {
1030 if ((count = [ctxStack count]) > 0) {
1031 [ctxStack removeObjectAtIndex:(count - 1)];
1037 obj = [ctxStack objectAtIndex:(count - 1)];
1039 if (![obj isNotNull]) obj = nil;
1040 [self setObject:obj forKey:@"_"];
1046 [self debugWithFormat:@"WARNING: -popCursor called without cycle ctx !"];
1051 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1052 old, [self cursor]];
1055 return [old autorelease];
1059 NSMutableArray *ctxStack;
1061 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1063 if ((ctxStack = self->cycleContext) == nil)
1064 /* no cycle context setup for component ... */
1066 if ([ctxStack count] == 0)
1067 /* nothing contained in cycle context ... */
1070 return [self objectForKey:@"_"];
1073 @end /* WOComponent(Cursors) */