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 [self->queryParameters release];
134 [_selectedDate release];
138 /* query parameters */
140 - (void) _parseQueryString: (NSString *) _s
145 NSString *key, *value;
147 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
148 part = [e nextObject];
151 r = [part rangeOfString:@"="];
154 /* missing value of query parameter */
155 key = [part stringByUnescapingURL];
160 key = [[part substringToIndex:r.location] stringByUnescapingURL];
161 value = [[part substringFromIndex:(r.location + r.length)]
162 stringByUnescapingURL];
164 [self->queryParameters setObject:value forKey:key];
165 part = [e nextObject];
169 - (void) addKeepAliveFormValuesToQueryParameters
173 - (NSString *) queryParameterForKey: (NSString *) _key
175 return [[self _queryParameters] objectForKey:_key];
178 - (void) setQueryParameter: (NSString *) _param
179 forKey: (NSString *) _key
184 [[self _queryParameters] setObject: _param forKey: _key];
186 [[self _queryParameters] removeObjectForKey: _key];
190 - (NSMutableDictionary *) _queryParameters
192 // TODO: this code is weird, should use WORequest methods for parsing
198 if (self->queryParameters)
199 return self->queryParameters;
201 self->queryParameters = [[NSMutableDictionary alloc] initWithCapacity:8];
203 req = [[self context] request];
205 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
208 qs = [uri substringFromIndex:NSMaxRange(r)];
209 [self _parseQueryString:qs];
212 /* add form values */
213 [self addKeepAliveFormValuesToQueryParameters];
215 return self->queryParameters;
218 - (NSDictionary *) queryParameters
220 return [self _queryParameters];
223 - (NSDictionary *) queryParametersBySettingSelectedDate: (NSCalendarDate *) _date
225 NSMutableDictionary *qp;
227 qp = [[self queryParameters] mutableCopy];
228 [self setSelectedDateQueryParameter:_date inDictionary:qp];
229 return [qp autorelease];
232 - (void) setSelectedDateQueryParameter: (NSCalendarDate *) _newDate
233 inDictionary: (NSMutableDictionary *) _qp
236 [_qp setObject: [self dateStringForDate: _newDate] forKey: @"day"];
238 [_qp removeObjectForKey:@"day"];
241 - (NSString *) completeHrefForMethod: (NSString *) _method
245 NSString *qs, *qps, *href;
247 qp = [self queryParameters];
250 ctx = [self context];
251 qps = [ctx queryPathSeparator];
252 [ctx setQueryPathSeparator: @"&"];
253 qs = [ctx queryStringFromDictionary: qp];
254 [ctx setQueryPathSeparator: qps];
255 href = [_method stringByAppendingFormat:@"?%@", qs];
263 - (NSString *) ownMethodName
268 uri = [[[self context] request] uri];
270 /* first: cut off query parameters */
272 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
274 uri = [uri substringToIndex:r.location];
276 /* next: strip trailing slash */
278 if ([uri hasSuffix: @"/"])
279 uri = [uri substringToIndex: ([uri length] - 1)];
280 r = [uri rangeOfString:@"/" options: NSBackwardsSearch];
282 /* then: cut of last path component */
284 if (r.length == 0) // no slash? are we at root?
287 return [uri substringFromIndex: (r.location + 1)];
290 - (NSString *) userFolderPath
293 NSEnumerator *objects;
294 SOGoObject *currentObject;
297 ctx = [self context];
298 objects = [[ctx objectTraversalStack] objectEnumerator];
299 currentObject = [objects nextObject];
303 if ([currentObject isKindOfClass: [SOGoUserFolder class]])
306 currentObject = [objects nextObject];
308 return [[currentObject baseURLInContext:ctx] hostlessURL];
311 - (NSString *) applicationPath
313 SOGoObject *currentClient, *parent;
315 Class objectClass, groupFolderClass, userFolderClass;
318 groupFolderClass = [SOGoCustomGroupFolder class];
319 userFolderClass = [SOGoUserFolder class];
321 currentClient = [self clientObject];
322 objectClass = [currentClient class];
323 found = (objectClass == groupFolderClass || objectClass == userFolderClass);
324 while (!found && currentClient)
326 parent = [currentClient container];
327 objectClass = [parent class];
328 if (objectClass == groupFolderClass
329 || objectClass == userFolderClass)
332 currentClient = parent;
335 ctx = [self context];
337 return [[currentClient baseURLInContext:ctx] hostlessURL];
340 - (NSString *) resourcesPath
342 WOResourceManager *rm;
344 if ((rm = [self resourceManager]) == nil)
345 rm = [[WOApplication application] resourceManager];
347 return [rm webServerResourcesPath];
350 - (NSString *) ownPath
355 uri = [[[self context] request] uri];
357 /* first: cut off query parameters */
359 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
361 uri = [uri substringToIndex:r.location];
366 - (NSString *) relativePathToUserFolderSubPath: (NSString *) _sub
370 dst = [[self userFolderPath] stringByAppendingPathComponent: _sub];
371 rel = [dst urlPathRelativeToPath:[self ownPath]];
376 - (NSCalendarDate *) selectedDate
378 NSTimeZone *userTimeZone;
382 userTimeZone = [[context activeUser] timeZone];
385 dateFromShortDateString: [self queryParameterForKey: @"day"]
386 andShortTimeString: [self queryParameterForKey: @"hm"]
387 inTimeZone: userTimeZone];
388 [_selectedDate retain];
391 return _selectedDate;
394 - (NSString *) dateStringForDate: (NSCalendarDate *) _date
396 NSTimeZone *userTimeZone;
398 userTimeZone = [[context activeUser] timeZone];
399 [_date setTimeZone: userTimeZone];
401 return [_date descriptionWithCalendarFormat:@"%Y%m%d"];
406 return ([[self queryParameterForKey: @"noframe"] boolValue]);
409 - (UIxComponent *) jsCloseWithRefreshMethod: (NSString *) methodName
413 jsClose = [UIxJSClose new];
414 [jsClose autorelease];
415 [jsClose setRefreshMethod: methodName];
426 ctx = [self context];
428 return [[[self clientObject] authenticatorInContext: ctx] userInContext: ctx];
431 - (NSString *) shortUserNameForDisplay
433 // TODO: better use a SoUser formatter?
434 // TODO: who calls that?
438 // TODO: USE USER MANAGER INSTEAD!
440 s = [[self user] login];
444 // TODO: algorithm might be inappropriate, depends on the actual UID
446 r = [s rangeOfString:@"."];
450 return [s substringToIndex:r.location];
455 - (NSString *) labelForKey: (NSString *) _str
457 WOResourceManager *rm;
459 NSString *lKey, *lTable, *lVal;
462 if ([_str length] == 0)
465 /* lookup languages */
467 languages = [[self context] resourceLookupLanguages];
469 /* find resource manager */
471 if ((rm = [self pageResourceManager]) == nil)
472 rm = [[WOApplication application] resourceManager];
474 [self warnWithFormat:@"missing resource manager!"];
478 r = [_str rangeOfString:@"/"];
480 lTable = [_str substringToIndex:r.location];
481 lKey = [_str substringFromIndex:(r.location + r.length)];
489 if ([lKey hasPrefix:@"$"])
490 lKey = [self valueForKeyPath:[lKey substringFromIndex:1]];
492 if ([lTable hasPrefix:@"$"])
493 lTable = [self valueForKeyPath:[lTable substringFromIndex:1]];
496 if ([lVal hasPrefix:@"$"])
497 lVal = [self valueForKeyPath:[lVal substringFromIndex:1]];
502 return [rm stringForKey: lKey
504 withDefaultValue: lVal
505 languages: languages];
508 - (NSString *) localizedNameForDayOfWeek:(unsigned)_dayOfWeek {
509 NSString *key = [dayLabelKeys objectAtIndex:_dayOfWeek % 7];
510 return [self labelForKey:key];
513 - (NSString *)localizedAbbreviatedNameForDayOfWeek:(unsigned)_dayOfWeek {
514 NSString *key = [abbrDayLabelKeys objectAtIndex:_dayOfWeek % 7];
515 return [self labelForKey:key];
518 - (NSString *)localizedNameForMonthOfYear:(unsigned)_monthOfYear {
519 NSString *key = [monthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
520 return [self labelForKey:key];
523 - (NSString *)localizedAbbreviatedNameForMonthOfYear:(unsigned)_monthOfYear {
524 NSString *key = [abbrMonthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
525 return [self labelForKey:key];
528 /* HTTP method safety */
530 - (BOOL)isInvokedBySafeMethod {
531 // TODO: move to WORequest?
534 m = [[[self context] request] method];
535 if ([m isEqualToString:@"GET"]) return YES;
536 if ([m isEqualToString:@"HEAD"]) return YES;
542 - (NSDictionary *)locale {
543 /* we need no fallback here, as locale is guaranteed to be set by sogod */
544 return [[self context] valueForKey:@"locale"];
547 - (WOResourceManager *) pageResourceManager
549 WOResourceManager *rm;
551 if ((rm = [[[self context] page] resourceManager]) == nil)
552 rm = [[WOApplication application] resourceManager];
557 - (NSString *) urlForResourceFilename: (NSString *) filename
559 static NSMutableDictionary *pageToURL = nil;
562 WOResourceManager *rm;
563 NSBundle *pageBundle;
568 pageToURL = [[NSMutableDictionary alloc] initWithCapacity: 32];
570 url = [pageToURL objectForKey: filename];
573 rm = [self pageResourceManager];
574 page = [[self context] page];
575 pageBundle = [NSBundle bundleForClass: [page class]];
576 url = [rm urlForResourceNamed: filename
577 inFramework: [pageBundle bundlePath]
579 request: [[self context] request]];
583 if ([url hasPrefix: @"http"])
584 url = [url hostlessURL];
585 [pageToURL setObject: url forKey: filename];
588 // NSLog (@"url for '%@': '%@'", filename, url);
598 - (BOOL)isUIxDebugEnabled {
599 return uixDebugEnabled;
602 @end /* UIxComponent */