2 Copyright (C) 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 #import <Foundation/NSDictionary.h>
23 #import <Foundation/NSException.h>
24 #import <Foundation/NSUserDefaults.h>
25 #import <Foundation/NSKeyValueCoding.h>
26 #import <Foundation/NSPathUtilities.h>
27 #import <NGObjWeb/SoHTTPAuthenticator.h>
28 #import <NGObjWeb/SoObjects.h>
29 #import <NGObjWeb/WOResourceManager.h>
30 #import <NGObjWeb/WORequest.h>
31 #import <NGObjWeb/WOContext+SoObjects.h>
32 #import <NGExtensions/NSObject+Logs.h>
33 #import <NGExtensions/NSString+misc.h>
34 #import <NGExtensions/NSURL+misc.h>
35 #import <SoObjects/SOGo/SOGoUser.h>
36 #import <SoObjects/SOGo/SOGoObject.h>
37 #import <SoObjects/SOGo/SOGoCustomGroupFolder.h>
38 #import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
39 #import <SoObjects/SOGo/NSString+Utilities.h>
41 #import "UIxComponent.h"
42 #import "UIxJSClose.h"
44 @interface UIxComponent (PrivateAPI)
45 - (void)_parseQueryString:(NSString *)_s;
46 - (NSMutableDictionary *)_queryParameters;
49 @implementation UIxComponent
51 static NSMutableArray *dayLabelKeys = nil;
52 static NSMutableArray *abbrDayLabelKeys = nil;
53 static NSMutableArray *monthLabelKeys = nil;
54 static NSMutableArray *abbrMonthLabelKeys = nil;
56 static BOOL uixDebugEnabled = NO;
59 return [super version] + 0 /* v2 */;
63 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
65 NSAssert2([super version] == 2,
66 @"invalid superclass (%@) version %i !",
67 NSStringFromClass([self superclass]), [super version]);
69 uixDebugEnabled = [ud boolForKey:@"SOGoUIxDebugEnabled"];
71 if (dayLabelKeys == nil) {
72 dayLabelKeys = [[NSMutableArray alloc] initWithCapacity:7];
73 [dayLabelKeys addObject:@"Sunday"];
74 [dayLabelKeys addObject:@"Monday"];
75 [dayLabelKeys addObject:@"Tuesday"];
76 [dayLabelKeys addObject:@"Wednesday"];
77 [dayLabelKeys addObject:@"Thursday"];
78 [dayLabelKeys addObject:@"Friday"];
79 [dayLabelKeys addObject:@"Saturday"];
81 abbrDayLabelKeys = [[NSMutableArray alloc] initWithCapacity:7];
82 [abbrDayLabelKeys addObject:@"a2_Sunday"];
83 [abbrDayLabelKeys addObject:@"a2_Monday"];
84 [abbrDayLabelKeys addObject:@"a2_Tuesday"];
85 [abbrDayLabelKeys addObject:@"a2_Wednesday"];
86 [abbrDayLabelKeys addObject:@"a2_Thursday"];
87 [abbrDayLabelKeys addObject:@"a2_Friday"];
88 [abbrDayLabelKeys addObject:@"a2_Saturday"];
90 monthLabelKeys = [[NSMutableArray alloc] initWithCapacity:12];
91 [monthLabelKeys addObject:@"January"];
92 [monthLabelKeys addObject:@"February"];
93 [monthLabelKeys addObject:@"March"];
94 [monthLabelKeys addObject:@"April"];
95 [monthLabelKeys addObject:@"May"];
96 [monthLabelKeys addObject:@"June"];
97 [monthLabelKeys addObject:@"July"];
98 [monthLabelKeys addObject:@"August"];
99 [monthLabelKeys addObject:@"September"];
100 [monthLabelKeys addObject:@"October"];
101 [monthLabelKeys addObject:@"November"];
102 [monthLabelKeys addObject:@"December"];
104 abbrMonthLabelKeys = [[NSMutableArray alloc] initWithCapacity:12];
105 [abbrMonthLabelKeys addObject:@"a3_January"];
106 [abbrMonthLabelKeys addObject:@"a3_February"];
107 [abbrMonthLabelKeys addObject:@"a3_March"];
108 [abbrMonthLabelKeys addObject:@"a3_April"];
109 [abbrMonthLabelKeys addObject:@"a3_May"];
110 [abbrMonthLabelKeys addObject:@"a3_June"];
111 [abbrMonthLabelKeys addObject:@"a3_July"];
112 [abbrMonthLabelKeys addObject:@"a3_August"];
113 [abbrMonthLabelKeys addObject:@"a3_September"];
114 [abbrMonthLabelKeys addObject:@"a3_October"];
115 [abbrMonthLabelKeys addObject:@"a3_November"];
116 [abbrMonthLabelKeys addObject:@"a3_December"];
122 if ((self = [super init]))
132 [queryParameters release];
133 [_selectedDate release];
137 /* query parameters */
139 - (void) _parseQueryString: (NSString *) _s
144 NSString *key, *value;
146 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
147 part = [e nextObject];
150 r = [part rangeOfString:@"="];
153 /* missing value of query parameter */
154 key = [part stringByUnescapingURL];
159 key = [[part substringToIndex:r.location] stringByUnescapingURL];
160 value = [[part substringFromIndex:(r.location + r.length)]
161 stringByUnescapingURL];
163 [queryParameters setObject:value forKey:key];
164 part = [e nextObject];
168 - (void) addKeepAliveFormValuesToQueryParameters
172 - (NSString *) queryParameterForKey: (NSString *) _key
174 return [[self _queryParameters] objectForKey:_key];
177 - (void) setQueryParameter: (NSString *) _param
178 forKey: (NSString *) _key
183 [[self _queryParameters] setObject: _param forKey: _key];
185 [[self _queryParameters] removeObjectForKey: _key];
189 - (NSMutableDictionary *) _queryParameters
191 // TODO: this code is weird, should use WORequest methods for parsing
198 return queryParameters;
200 queryParameters = [[NSMutableDictionary alloc] initWithCapacity:8];
202 req = [[self context] request];
204 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
207 qs = [uri substringFromIndex:NSMaxRange(r)];
208 [self _parseQueryString:qs];
211 /* add form values */
212 [self addKeepAliveFormValuesToQueryParameters];
214 return queryParameters;
217 - (NSDictionary *) queryParameters
219 return [self _queryParameters];
222 - (NSDictionary *) queryParametersBySettingSelectedDate: (NSCalendarDate *) _date
224 NSMutableDictionary *qp;
226 qp = [[self queryParameters] mutableCopy];
227 [self setSelectedDateQueryParameter:_date inDictionary:qp];
228 return [qp autorelease];
231 - (void) setSelectedDateQueryParameter: (NSCalendarDate *) _newDate
232 inDictionary: (NSMutableDictionary *) _qp
235 [_qp setObject: [self dateStringForDate: _newDate] forKey: @"day"];
237 [_qp removeObjectForKey:@"day"];
240 - (NSString *) completeHrefForMethod: (NSString *) _method
244 NSString *qs, *qps, *href;
246 qp = [self queryParameters];
249 ctx = [self context];
250 qps = [ctx queryPathSeparator];
251 [ctx setQueryPathSeparator: @"&"];
252 qs = [ctx queryStringFromDictionary: qp];
253 [ctx setQueryPathSeparator: qps];
254 href = [_method stringByAppendingFormat:@"?%@", qs];
262 - (NSString *) ownMethodName
267 uri = [[[self context] request] uri];
269 /* first: cut off query parameters */
271 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
273 uri = [uri substringToIndex:r.location];
275 /* next: strip trailing slash */
277 if ([uri hasSuffix: @"/"])
278 uri = [uri substringToIndex: ([uri length] - 1)];
279 r = [uri rangeOfString:@"/" options: NSBackwardsSearch];
281 /* then: cut of last path component */
283 if (r.length == 0) // no slash? are we at root?
286 return [uri substringFromIndex: (r.location + 1)];
289 - (NSString *) userFolderPath
292 NSEnumerator *objects;
293 SOGoObject *currentObject;
296 ctx = [self context];
297 objects = [[ctx objectTraversalStack] objectEnumerator];
298 currentObject = [objects nextObject];
302 if ([currentObject isKindOfClass: [SOGoUserFolder class]])
305 currentObject = [objects nextObject];
307 return [[currentObject baseURLInContext:ctx] hostlessURL];
310 - (NSString *) applicationPath
312 SOGoObject *currentClient, *parent;
314 Class objectClass, groupFolderClass, userFolderClass;
317 groupFolderClass = [SOGoCustomGroupFolder class];
318 userFolderClass = [SOGoUserFolder class];
320 currentClient = [self clientObject];
321 objectClass = [currentClient class];
322 found = (objectClass == groupFolderClass || objectClass == userFolderClass);
323 while (!found && currentClient)
325 parent = [currentClient container];
326 objectClass = [parent class];
327 if (objectClass == groupFolderClass
328 || objectClass == userFolderClass)
331 currentClient = parent;
334 ctx = [self context];
336 return [[currentClient baseURLInContext:ctx] hostlessURL];
339 - (NSString *) resourcesPath
341 WOResourceManager *rm;
343 if ((rm = [self resourceManager]) == nil)
344 rm = [[WOApplication application] resourceManager];
346 return [rm webServerResourcesPath];
349 - (NSString *) ownPath
354 uri = [[[self context] request] uri];
356 /* first: cut off query parameters */
358 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
360 uri = [uri substringToIndex:r.location];
365 - (NSString *) relativePathToUserFolderSubPath: (NSString *) _sub
369 dst = [[self userFolderPath] stringByAppendingPathComponent: _sub];
370 rel = [dst urlPathRelativeToPath:[self ownPath]];
375 - (NSCalendarDate *) selectedDate
377 NSTimeZone *userTimeZone;
381 userTimeZone = [[context activeUser] timeZone];
384 dateFromShortDateString: [self queryParameterForKey: @"day"]
385 andShortTimeString: [self queryParameterForKey: @"hm"]
386 inTimeZone: userTimeZone];
387 [_selectedDate retain];
390 return _selectedDate;
393 - (NSString *) dateStringForDate: (NSCalendarDate *) _date
395 NSTimeZone *userTimeZone;
397 userTimeZone = [[context activeUser] timeZone];
398 [_date setTimeZone: userTimeZone];
400 return [_date descriptionWithCalendarFormat:@"%Y%m%d"];
405 return ([[self queryParameterForKey: @"noframe"] boolValue]);
408 - (UIxComponent *) jsCloseWithRefreshMethod: (NSString *) methodName
412 jsClose = [UIxJSClose new];
413 [jsClose autorelease];
414 [jsClose setRefreshMethod: methodName];
425 ctx = [self context];
427 return [[[self clientObject] authenticatorInContext: ctx] userInContext: ctx];
430 - (NSString *) shortUserNameForDisplay
432 // TODO: better use a SoUser formatter?
433 // TODO: who calls that?
437 // TODO: USE USER MANAGER INSTEAD!
439 s = [[self user] login];
443 // TODO: algorithm might be inappropriate, depends on the actual UID
445 r = [s rangeOfString:@"."];
449 return [s substringToIndex:r.location];
454 - (NSString *) labelForKey: (NSString *) _str
456 WOResourceManager *rm;
458 NSString *lKey, *lTable, *lVal;
461 if ([_str length] == 0)
464 /* lookup languages */
466 languages = [[self context] resourceLookupLanguages];
468 /* find resource manager */
470 if ((rm = [self pageResourceManager]) == nil)
471 rm = [[WOApplication application] resourceManager];
473 [self warnWithFormat:@"missing resource manager!"];
477 r = [_str rangeOfString:@"/"];
479 lTable = [_str substringToIndex:r.location];
480 lKey = [_str substringFromIndex:(r.location + r.length)];
488 if ([lKey hasPrefix:@"$"])
489 lKey = [self valueForKeyPath:[lKey substringFromIndex:1]];
491 if ([lTable hasPrefix:@"$"])
492 lTable = [self valueForKeyPath:[lTable substringFromIndex:1]];
495 if ([lVal hasPrefix:@"$"])
496 lVal = [self valueForKeyPath:[lVal substringFromIndex:1]];
501 return [rm stringForKey: lKey
503 withDefaultValue: lVal
504 languages: languages];
507 - (NSString *) localizedNameForDayOfWeek:(unsigned)_dayOfWeek {
508 NSString *key = [dayLabelKeys objectAtIndex:_dayOfWeek % 7];
509 return [self labelForKey:key];
512 - (NSString *)localizedAbbreviatedNameForDayOfWeek:(unsigned)_dayOfWeek {
513 NSString *key = [abbrDayLabelKeys objectAtIndex:_dayOfWeek % 7];
514 return [self labelForKey:key];
517 - (NSString *)localizedNameForMonthOfYear:(unsigned)_monthOfYear {
518 NSString *key = [monthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
519 return [self labelForKey:key];
522 - (NSString *)localizedAbbreviatedNameForMonthOfYear:(unsigned)_monthOfYear {
523 NSString *key = [abbrMonthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
524 return [self labelForKey:key];
527 /* HTTP method safety */
529 - (BOOL)isInvokedBySafeMethod {
530 // TODO: move to WORequest?
533 m = [[[self context] request] method];
534 if ([m isEqualToString:@"GET"]) return YES;
535 if ([m isEqualToString:@"HEAD"]) return YES;
541 - (NSDictionary *)locale {
542 /* we need no fallback here, as locale is guaranteed to be set by sogod */
543 return [context valueForKey: @"locale"];
546 - (WOResourceManager *) pageResourceManager
548 WOResourceManager *rm;
550 if ((rm = [[[self context] page] resourceManager]) == nil)
551 rm = [[WOApplication application] resourceManager];
556 - (NSString *) urlForResourceFilename: (NSString *) filename
558 static NSMutableDictionary *pageToURL = nil;
561 WOResourceManager *rm;
562 NSBundle *pageBundle;
567 pageToURL = [[NSMutableDictionary alloc] initWithCapacity: 32];
569 url = [pageToURL objectForKey: filename];
572 rm = [self pageResourceManager];
573 page = [[self context] page];
574 pageBundle = [NSBundle bundleForClass: [page class]];
575 url = [rm urlForResourceNamed: filename
576 inFramework: [pageBundle bundlePath]
578 request: [[self context] request]];
582 if ([url hasPrefix: @"http"])
583 url = [url hostlessURL];
584 [pageToURL setObject: url forKey: filename];
587 // NSLog (@"url for '%@': '%@'", filename, url);
597 - (BOOL)isUIxDebugEnabled {
598 return uixDebugEnabled;
601 @end /* UIxComponent */