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) {
489 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
493 if ((url = [NSURL URLWithString:serverURL]) == nil) {
494 [self logWithFormat:@"could not construct NSURL from string '%@'",
506 return self->baseURL;
508 if ((rq = [self request]) == nil) {
509 [self logWithFormat:@"missing request in -baseURL call .."];
513 serverURL = [self serverURL];
515 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
517 if (self->baseURL == nil) {
519 @"could not construct NSURL for uri '%@' and base '%@' ...",
520 [rq uri], serverURL];
522 return self->baseURL;
525 - (NSURL *)applicationURL {
528 if (self->appURL != nil)
531 // TODO: we should ensure that the suffix (.woa) is in the URL
533 s = [self->request adaptorPrefix];
534 if ([s length] > 0) {
535 s = [[[s stringByAppendingString:@"/"]
536 stringByAppendingString:[self->request applicationName]]
537 stringByAppendingString:@"/"];
540 s = [[self->request applicationName] stringByAppendingString:@"/"];
543 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
546 - (NSURL *)urlForKey:(NSString *)_key {
547 _key = [_key stringByAppendingString:@"/"];
548 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
553 - (void)setInForm:(BOOL)_form {
554 self->inForm = _form;
560 - (void)addActiveFormElement:(WOElement *)_formElement {
561 if (self->activeFormElement) {
562 [[self component] debugWithFormat:@"active form element already set !"];
566 ASSIGN(self->activeFormElement, _formElement);
567 [self setRequestSenderID:[self elementID]];
569 - (WOElement *)activeFormElement {
570 return self->activeFormElement;
573 /* context variables (transient) */
575 - (void)setObject:(id)_obj forKey:(NSString *)_key {
576 if (self->variables == nil) {
578 [[NSMutableDictionary allocWithZone:[self zone]]
579 initWithCapacity:16];
583 [self->variables setObject:_obj forKey:_key];
585 [self->variables removeObjectForKey:_key];
587 - (id)objectForKey:(NSString *)_key {
588 return [self->variables objectForKey:_key];
590 - (void)removeObjectForKey:(NSString *)_key {
591 [self->variables removeObjectForKey:_key];
594 - (NSDictionary *)variableDictionary {
595 return self->variables;
598 - (void)takeValue:(id)_value forKey:(NSString *)_key {
599 if (WOSetKVCValueUsingMethod(self, _key, _value))
602 else if (WOGetKVCGetMethod(self, _key) == NULL) {
604 _value = [EONull null];
606 if (self->variables == nil) {
608 [[NSMutableDictionary allocWithZone:[self zone]]
609 initWithCapacity:16];
611 [self->variables setObject:_value forKey:_key];
615 // only a 'get' method is defined for _key !
616 [self handleTakeValue:_value forUnboundKey:_key];
619 - (id)valueForKey:(NSString *)_key {
622 if ((value = WOGetKVCValueUsingMethod(self, _key)))
624 value = [self->variables objectForKey:_key];
630 - (id)copyWithZone:(NSZone *)_zone {
631 return [self retain];
636 - (NSString *)description {
638 WOApplication *app = [self application];
640 if ([self hasSession])
641 sid = [[self session] sessionID];
643 return [NSString stringWithFormat:
644 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
645 (unsigned)self, NSStringFromClass([self class]),
655 @implementation WOContext(ElementIDs)
657 - (NSString *)elementID {
658 return [self->elementID elementID];
660 - (void)appendElementIDComponent:(NSString *)_eid {
661 [self->elementID appendElementIDComponent:_eid];
663 - (void)appendZeroElementIDComponent {
664 [self->elementID appendZeroElementIDComponent];
666 - (void)deleteAllElementIDComponents {
667 [self->elementID deleteAllElementIDComponents];
669 - (void)deleteLastElementIDComponent {
670 [self->elementID deleteLastElementIDComponent];
672 - (void)incrementLastElementIDComponent {
673 [self->elementID incrementLastElementIDComponent];
675 - (void)appendIntElementIDComponent:(int)_eid {
676 [self->elementID appendIntElementIDComponent:_eid];
679 /* the following can be later moved to WOElementID */
681 - (id)currentElementID {
682 return [self->reqElementID currentElementID];
684 - (id)consumeElementID {
685 return [self->reqElementID consumeElementID];
688 @end /* WOContext(ElementIDs) */
690 @implementation WOContext(URLs)
692 - (void)_generateCompleteURLs {
693 /* described in Apple TIL article 70101 */
696 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
702 qs = [MutableStrClass stringWithCapacity:256];
703 keys = [_queryDict keyEnumerator];
704 for (isFirst = YES; (key = [keys nextObject]); ) {
710 [qs appendString:self->qpJoin];
712 value = [[_queryDict objectForKey:key] stringValue];
714 key = [key stringByEscapingURL];
715 value = [value stringByEscapingURL];
717 [qs appendString:key];
719 [qs appendString:@"="];
720 [qs appendString:value];
727 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
728 queryDictionary:(NSDictionary *)_queryDict
730 NSMutableString *url;
733 url = [MutableStrClass stringWithCapacity:256];
736 [url appendString:@"/"];
738 [url appendString:_actionName];
740 /* add query parameters */
742 qs = [self queryStringFromDictionary:_queryDict];
744 return [self urlWithRequestHandlerKey:
745 [WOAppClass directActionRequestHandlerKey]
746 path:url queryString:qs];
749 - (NSString *)componentActionURL {
750 // TODO: add a -cComponentActionURL
751 // (without NSString for use with appendContentCString:)
753 // 26% -urlWithRequestHandler...
754 // 21% -elementID (was 40% !! :-)
755 // ~20% mutable string ops
760 self->savePageRequired = YES;
761 qs = [MutableStrClass stringWithCapacity:64];
763 [qs appendString:WORequestValueSenderID];
764 [qs appendString:@"="];
765 [qs appendString:[self elementID]];
766 [qs appendString:self->qpJoin];
767 [qs appendString:WORequestValueSessionID];
768 [qs appendString:@"="];
769 [qs appendString:[[self session] sessionID]];
770 [qs appendString:self->qpJoin];
771 [qs appendString:WORequestValueContextID];
772 [qs appendString:@"="];
773 [qs appendString:[self contextID]];
775 p = [[self page] componentActionURLForContext:self];
778 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
781 return [self urlWithRequestHandlerKey:
782 [WOAppClass componentRequestHandlerKey]
787 /* old style URLs ... */
788 static NSMutableString *url = nil; // THREAD
789 static IMP addStr = NULL;
792 self->savePageRequired = YES;
794 url = [[MutableStrClass alloc] initWithCapacity:256];
795 addStr = [url methodForSelector:@selector(appendString:)];
796 addStr(url, @selector(appendString:), @"/");
799 [url setString:@"/"];
802 Note: component actions *always* require sessions to be able to locate
803 the request component !
805 addStr(url, @selector(appendString:), [[self session] sessionID]);
806 addStr(url, @selector(appendString:), @"/");
807 addStr(url, @selector(appendString:), [self->elementID elementID]);
809 s = [self urlWithRequestHandlerKey:
810 [WOAppClass componentRequestHandlerKey]
811 path:url queryString:nil];
816 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
817 path:(NSString *)_path
818 queryString:(NSString *)_query
820 if (testNSURLs) { /* use NSURLs for processing */
823 if ([_path hasPrefix:@"/"]) {
825 [self warnWithFormat:@"got absolute path '%@'", _path];
827 _path = [_path substringFromIndex:1];
830 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
831 rqUrl = [self urlForKey:_key];
833 if ([_query length] > 0) {
836 s = [_path mutableCopy];
837 [s appendString:@"?"];
838 [s appendString:_query];
839 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
843 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
845 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
847 return [rqUrl stringValueRelativeToURL:[self baseURL]];
850 NSMutableString *url;
854 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
856 url = [MutableStrClass stringWithCapacity:256];
857 addStr = [url methodForSelector:@selector(appendString:)];
860 if (self->urlPrefix == nil) {
862 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
863 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
864 tmp = [tmp substringToIndex:([tmp length] - 2)];
865 addStr(url, @selector(appendString:), tmp);
867 else if ((tmp = [self->request headerForKey:@"host"])) {
868 addStr(url, @selector(appendString:), @"http://");
869 addStr(url, @selector(appendString:), tmp);
873 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
874 addStr(url, @selector(appendString:), @"/");
875 tmp = [[self request] applicationName];
876 if ([tmp length] == 0)
877 tmp = [(WOApplication *)[self application] name];
878 if ([tmp length] > 0) {
879 addStr(url, @selector(appendString:), tmp);
880 if (WOApplicationSuffix)
881 addStr(url, @selector(appendString:), WOApplicationSuffix);
882 addStr(url, @selector(appendString:), @"/");
886 self->urlPrefix = [url copy];
887 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
890 /* prefix is cached :-) */
891 addStr(url, @selector(appendString:), self->urlPrefix);
895 addStr(url, @selector(appendString:), _key);
897 addStr(url, @selector(appendString:), _path);
898 if ([_query length] > 0) {
899 addStr(url, @selector(appendString:), @"?");
900 addStr(url, @selector(appendString:), _query);
905 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
906 path:(NSString *)_path queryString:(NSString *)_query
907 isSecure:(BOOL)_isSecure port:(int)_port
909 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
910 [url appendString:_isSecure ? @"https://" : @"http://"];
911 [url appendString:[[self request] headerForKey:@"host"]];
913 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
914 [url appendFormat:@":%i", _port];
916 [url appendString:[self urlWithRequestHandlerKey:_key
918 queryString:_query]];
922 - (void)setRequestSenderID:(NSString *)_rid {
925 eid = [[WOElementID alloc] initWithString:_rid];
926 [self->reqElementID release];
927 self->reqElementID = eid;
929 - (NSString *)senderID {
931 return [self->reqElementID elementID];
933 NSMutableString *eid;
937 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
938 addStr = [eid methodForSelector:@selector(appendString:)];
939 for (i = 0; i < self->reqElementIdCount; i++) {
940 if (i != 0) addStr(eid, @selector(appendString:), @".");
941 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
947 @end /* WOContext(URLs) */
949 @implementation WOContext(DeprecatedMethodsInWO4)
951 - (WOApplication *)application {
952 if (self->application == nil)
953 self->application = [WOAppClass application];
955 if (self->application == nil)
956 NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self);
958 return self->application;
961 - (void)setDistributionEnabled:(BOOL)_flag {
963 [[self session] setDistributionEnabled:_flag];
965 - (BOOL)isDistributionEnabled {
967 return [[self session] isDistributionEnabled];
971 return [self componentActionURL];
974 - (NSString *)urlSessionPrefix {
975 NSMutableString *url;
978 url = [MutableStrClass stringWithCapacity:128];
980 [url appendString:[[self request] adaptorPrefix]];
981 [url appendString:@"/"];
982 tmp = [[self request] applicationName];
984 tmp ? tmp : [(WOApplication *)[self application] name]];
987 if ([url length] == 0) {
988 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
989 __PRETTY_FUNCTION__];
996 @end /* WOContext(DeprecatedMethodsInWO4) */
998 @implementation WOComponent(Cursors)
1000 - (void)pushCursor:(id)_obj {
1001 NSMutableArray *ctxStack;
1004 [self logWithFormat:@"enter cursor: %@", _obj];
1006 if ((ctxStack = self->cycleContext) == nil)
1007 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1009 /* add to cursor stack */
1010 [ctxStack addObject:(_obj ? _obj : [NSNull null])];
1012 /* set active cursor */
1013 if (![_obj isNotNull]) _obj = nil;
1014 [self setObject:_obj forKey:@"_"];
1018 NSMutableArray *ctxStack;
1021 /* retrieve last context */
1022 old = [[self objectForKey:@"_"] retain];
1023 [self setObject:nil forKey:@"_"];
1025 /* restore old ctx */
1026 if ((ctxStack = self->cycleContext) != nil) {
1029 if ((count = [ctxStack count]) > 0) {
1030 [ctxStack removeObjectAtIndex:(count - 1)];
1036 obj = [ctxStack objectAtIndex:(count - 1)];
1038 if (![obj isNotNull]) obj = nil;
1039 [self setObject:obj forKey:@"_"];
1045 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1050 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1051 old, [self cursor]];
1054 return [old autorelease];
1058 NSMutableArray *ctxStack;
1060 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1062 if ((ctxStack = self->cycleContext) == nil)
1063 /* no cycle context setup for component ... */
1065 if ([ctxStack count] == 0)
1066 /* nothing contained in cycle context ... */
1069 return [self objectForKey:@"_"];
1072 @end /* WOComponent(Cursors) */