]> err.no Git - scalable-opengroupware.org/blob - UI/SOGoUI/UIxComponent.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1137 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / SOGoUI / UIxComponent.m
1 /*
2   Copyright (C) 2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
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>
40
41 #import "UIxComponent.h"
42 #import "UIxJSClose.h"
43
44 @interface UIxComponent (PrivateAPI)
45 - (void)_parseQueryString:(NSString *)_s;
46 - (NSMutableDictionary *)_queryParameters;
47 @end
48
49 @implementation UIxComponent
50
51 static NSMutableArray *dayLabelKeys       = nil;
52 static NSMutableArray *abbrDayLabelKeys   = nil;
53 static NSMutableArray *monthLabelKeys     = nil;
54 static NSMutableArray *abbrMonthLabelKeys = nil;
55
56 static BOOL uixDebugEnabled = NO;
57
58 + (int)version {
59   return [super version] + 0 /* v2 */;
60 }
61
62 + (void)initialize {
63   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
64
65   NSAssert2([super version] == 2,
66             @"invalid superclass (%@) version %i !",
67             NSStringFromClass([self superclass]), [super version]);
68   
69   uixDebugEnabled = [ud boolForKey:@"SOGoUIxDebugEnabled"];
70
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"];
80
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"];
89
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"];
103
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"];
117   }
118 }
119
120 - (id) init
121 {
122   if ((self = [super init]))
123     {
124       _selectedDate = nil;
125     }
126
127   return self;
128 }
129
130 - (void) dealloc
131 {
132   [queryParameters release];
133   [_selectedDate release];
134   [super dealloc];
135 }
136
137 /* query parameters */
138
139 - (void) _parseQueryString: (NSString *) _s
140 {
141   NSEnumerator *e;
142   NSString *part;
143   NSRange  r;
144   NSString *key, *value;
145
146   e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
147   part = [e nextObject];
148   while (part)
149     {
150       r = [part rangeOfString:@"="];
151       if (r.length == 0)
152         {
153       /* missing value of query parameter */
154           key   = [part stringByUnescapingURL];
155           value = @"1";
156         }
157       else
158         {
159           key   = [[part substringToIndex:r.location] stringByUnescapingURL];
160           value = [[part substringFromIndex:(r.location + r.length)] 
161                     stringByUnescapingURL];
162         }
163       [queryParameters setObject:value forKey:key];
164       part = [e nextObject];
165     }
166 }
167
168 - (void) addKeepAliveFormValuesToQueryParameters
169 {
170 }
171
172 - (NSString *) queryParameterForKey: (NSString *) _key
173 {
174   return [[self _queryParameters] objectForKey:_key];
175 }
176
177 - (void) setQueryParameter: (NSString *) _param
178                     forKey: (NSString *) _key
179 {
180   if (_key)
181     {
182       if (_param)
183         [[self _queryParameters] setObject: _param forKey: _key];
184       else
185         [[self _queryParameters] removeObjectForKey: _key];
186     }
187 }
188
189 - (NSMutableDictionary *) _queryParameters
190 {
191   // TODO: this code is weird, should use WORequest methods for parsing
192   WORequest *req;
193   NSString  *uri;
194   NSRange   r;
195   NSString *qs;
196   
197   if (queryParameters)
198     return queryParameters;
199
200   queryParameters = [[NSMutableDictionary alloc] initWithCapacity:8];
201
202   req = [[self context] request];
203   uri = [req uri];
204   r   = [uri rangeOfString:@"?" options:NSBackwardsSearch];
205   if (r.length > 0)
206     {
207       qs = [uri substringFromIndex:NSMaxRange(r)];
208       [self _parseQueryString:qs];
209     }
210   
211   /* add form values */
212   [self addKeepAliveFormValuesToQueryParameters];
213
214   return queryParameters;
215 }
216
217 - (NSDictionary *) queryParameters
218 {
219   return [self _queryParameters];
220 }
221
222 - (NSDictionary *) queryParametersBySettingSelectedDate: (NSCalendarDate *) _date
223 {
224   NSMutableDictionary *qp;
225     
226   qp = [[self queryParameters] mutableCopy];
227   [self setSelectedDateQueryParameter:_date inDictionary:qp];
228   return [qp autorelease];
229 }
230
231 - (void) setSelectedDateQueryParameter: (NSCalendarDate *) _newDate
232                           inDictionary: (NSMutableDictionary *) _qp
233 {
234   if (_newDate)
235     [_qp setObject: [self dateStringForDate: _newDate] forKey: @"day"];
236   else
237     [_qp removeObjectForKey:@"day"];
238 }
239
240 - (NSString *) completeHrefForMethod: (NSString *) _method
241 {
242   WOContext *ctx;
243   NSDictionary *qp;
244   NSString *qs, *qps, *href;
245
246   qp = [self queryParameters];
247   if ([qp count] > 0)
248     {
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];
255     }
256   else
257     href = _method;
258
259   return href;
260 }
261
262 - (NSString *) ownMethodName
263 {
264   NSString *uri;
265   NSRange  r;
266     
267   uri = [[[self context] request] uri];
268     
269   /* first: cut off query parameters */
270     
271   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
272   if (r.length > 0)
273     uri = [uri substringToIndex:r.location];
274     
275   /* next: strip trailing slash */
276
277   if ([uri hasSuffix: @"/"])
278     uri = [uri substringToIndex: ([uri length] - 1)];
279   r = [uri rangeOfString:@"/" options: NSBackwardsSearch];
280     
281   /* then: cut of last path component */
282     
283   if (r.length == 0) // no slash? are we at root?
284     return @"/";
285     
286   return [uri substringFromIndex: (r.location + 1)];
287 }
288
289 - (NSString *) userFolderPath
290 {
291   WOContext *ctx;
292   NSEnumerator *objects;
293   SOGoObject *currentObject;
294   BOOL found;
295
296   ctx = [self context];
297   objects = [[ctx objectTraversalStack] objectEnumerator];
298   currentObject = [objects nextObject];
299   found = NO;
300   while (currentObject
301          && !found)
302     if ([currentObject isKindOfClass: [SOGoUserFolder class]])
303       found = YES;
304     else
305       currentObject = [objects nextObject];
306
307   return [[currentObject baseURLInContext:ctx] hostlessURL];
308 }
309
310 - (NSString *) applicationPath
311 {
312   SOGoObject *currentClient, *parent;
313   BOOL found;
314   Class objectClass, groupFolderClass, userFolderClass;
315   WOContext *ctx;
316
317   groupFolderClass = [SOGoCustomGroupFolder class];
318   userFolderClass = [SOGoUserFolder class];
319
320   currentClient = [self clientObject];
321   objectClass = [currentClient class];
322   found = (objectClass == groupFolderClass || objectClass == userFolderClass);
323   while (!found && currentClient)
324     {
325       parent = [currentClient container];
326       objectClass = [parent class];
327       if (objectClass == groupFolderClass
328           || objectClass == userFolderClass)
329         found = YES;
330       else
331         currentClient = parent;
332     }
333
334   ctx = [self context];
335
336   return [[currentClient baseURLInContext:ctx] hostlessURL];
337 }
338
339 - (NSString *) resourcesPath
340 {
341   WOResourceManager *rm;
342
343   if ((rm = [self resourceManager]) == nil)
344     rm = [[WOApplication application] resourceManager];
345
346   return [rm webServerResourcesPath];
347 }
348
349 - (NSString *) ownPath
350 {
351   NSString *uri;
352   NSRange  r;
353   
354   uri = [[[self context] request] uri];
355   
356   /* first: cut off query parameters */
357   
358   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
359   if (r.length > 0)
360     uri = [uri substringToIndex:r.location];
361
362   return uri;
363 }
364
365 - (NSString *) relativePathToUserFolderSubPath: (NSString *) _sub
366 {
367   NSString *dst, *rel;
368
369   dst = [[self userFolderPath] stringByAppendingPathComponent: _sub];
370   rel = [dst urlPathRelativeToPath:[self ownPath]];
371
372   return rel;
373 }
374
375 - (NSCalendarDate *) selectedDate
376 {
377   NSTimeZone *userTimeZone;
378
379   if (!_selectedDate)
380     {
381       userTimeZone = [[context activeUser] timeZone];
382       _selectedDate
383         = [NSCalendarDate
384             dateFromShortDateString: [self queryParameterForKey: @"day"]
385             andShortTimeString: [self queryParameterForKey: @"hm"]
386             inTimeZone: userTimeZone];
387       [_selectedDate retain];
388     }
389
390   return _selectedDate;
391 }
392
393 - (NSString *) dateStringForDate: (NSCalendarDate *) _date
394 {
395   NSTimeZone *userTimeZone;
396
397   userTimeZone = [[context activeUser] timeZone];
398   [_date setTimeZone: userTimeZone];
399
400   return [_date descriptionWithCalendarFormat:@"%Y%m%d"];
401 }
402
403 - (BOOL) hideFrame
404 {
405   return ([[self queryParameterForKey: @"noframe"] boolValue]);
406 }
407
408 - (UIxComponent *) jsCloseWithRefreshMethod: (NSString *) methodName
409 {
410   UIxJSClose *jsClose;
411
412   jsClose = [UIxJSClose new];
413   [jsClose autorelease];
414   [jsClose setRefreshMethod: methodName];
415
416   return jsClose;
417 }
418
419 /* SoUser */
420
421 - (SoUser *) user
422 {
423   WOContext *ctx;
424   
425   ctx = [self context];
426
427   return [[[self clientObject] authenticatorInContext: ctx] userInContext: ctx];
428 }
429
430 - (NSString *) shortUserNameForDisplay
431 {
432   // TODO: better use a SoUser formatter?
433   // TODO: who calls that?
434   NSString *s;
435   NSRange  r;
436   
437   // TODO: USE USER MANAGER INSTEAD!
438   
439   s = [[self user] login];
440   if ([s length] < 10)
441     return s;
442     
443   // TODO: algorithm might be inappropriate, depends on the actual UID
444     
445   r = [s rangeOfString:@"."];
446   if (r.length == 0)
447     return s;
448     
449   return [s substringToIndex:r.location];
450 }
451
452 /* labels */
453
454 - (NSString *) labelForKey: (NSString *) _str
455 {
456   WOResourceManager *rm;
457   NSArray *languages;
458   NSString *lKey, *lTable, *lVal;
459   NSRange r;
460
461   if ([_str length] == 0)
462     return nil;
463   
464   /* lookup languages */
465     
466   languages = [[self context] resourceLookupLanguages];
467     
468   /* find resource manager */
469     
470   if ((rm = [self pageResourceManager]) == nil)
471     rm = [[WOApplication application] resourceManager];
472   if (rm == nil)
473     [self warnWithFormat:@"missing resource manager!"];
474     
475   /* get parameters */
476     
477   r = [_str rangeOfString:@"/"];
478   if (r.length > 0) {
479     lTable = [_str substringToIndex:r.location];
480     lKey   = [_str substringFromIndex:(r.location + r.length)];
481   }
482   else {
483     lTable = nil;
484     lKey   = _str;
485   }
486   lVal = lKey;
487
488   if ([lKey hasPrefix:@"$"])
489     lKey = [self valueForKeyPath:[lKey substringFromIndex:1]];
490   
491   if ([lTable hasPrefix:@"$"])
492     lTable = [self valueForKeyPath:[lTable substringFromIndex:1]];
493   
494 #if 0
495   if ([lVal hasPrefix:@"$"])
496     lVal = [self valueForKeyPath:[lVal substringFromIndex:1]];
497   
498 #endif
499   
500   /* lookup string */
501   return [rm stringForKey: lKey
502              inTableNamed: lTable
503              withDefaultValue: lVal
504              languages: languages];
505 }
506
507 - (NSString *) localizedNameForDayOfWeek:(unsigned)_dayOfWeek {
508   NSString *key =  [dayLabelKeys objectAtIndex:_dayOfWeek % 7];
509   return [self labelForKey:key];
510 }
511
512 - (NSString *)localizedAbbreviatedNameForDayOfWeek:(unsigned)_dayOfWeek {
513   NSString *key =  [abbrDayLabelKeys objectAtIndex:_dayOfWeek % 7];
514   return [self labelForKey:key];
515 }
516
517 - (NSString *)localizedNameForMonthOfYear:(unsigned)_monthOfYear {
518   NSString *key =  [monthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
519   return [self labelForKey:key];
520 }
521
522 - (NSString *)localizedAbbreviatedNameForMonthOfYear:(unsigned)_monthOfYear {
523   NSString *key =  [abbrMonthLabelKeys objectAtIndex:(_monthOfYear - 1) % 12];
524   return [self labelForKey:key];
525 }
526
527 /* HTTP method safety */
528
529 - (BOOL)isInvokedBySafeMethod {
530   // TODO: move to WORequest?
531   NSString *m;
532   
533   m = [[[self context] request] method];
534   if ([m isEqualToString:@"GET"])  return YES;
535   if ([m isEqualToString:@"HEAD"]) return YES;
536   return NO;
537 }
538
539 /* locale */
540
541 - (NSDictionary *)locale {
542   /* we need no fallback here, as locale is guaranteed to be set by sogod */
543   return [context valueForKey: @"locale"];
544 }
545
546 - (WOResourceManager *) pageResourceManager
547 {
548   WOResourceManager *rm;
549   
550   if ((rm = [[[self context] page] resourceManager]) == nil)
551     rm = [[WOApplication application] resourceManager];
552
553   return rm;
554 }
555
556 - (NSString *) urlForResourceFilename: (NSString *) filename
557 {
558   static NSMutableDictionary *pageToURL = nil;
559   NSString *url;
560   WOComponent *page;
561   WOResourceManager *rm;
562   NSBundle *pageBundle;
563
564   if (filename)
565     {
566       if (!pageToURL)
567         pageToURL = [[NSMutableDictionary alloc] initWithCapacity: 32];
568
569       url = [pageToURL objectForKey: filename];
570       if (!url)
571         {
572           rm = [self pageResourceManager];
573           page = [[self context] page];
574           pageBundle = [NSBundle bundleForClass: [page class]];
575           url = [rm urlForResourceNamed: filename
576                     inFramework: [pageBundle bundlePath]
577                     languages: nil
578                     request: [[self context] request]];
579           if (!url)
580             url = @"";
581           else
582             if ([url hasPrefix: @"http"])
583               url = [url hostlessURL];
584           [pageToURL setObject: url forKey: filename];
585         }
586
587 //   NSLog (@"url for '%@': '%@'", filename, url);
588     }
589   else
590     url = @"";
591
592   return url;
593 }
594
595 /* debugging */
596
597 - (BOOL)isUIxDebugEnabled {
598   return uixDebugEnabled;
599 }
600
601 @end /* UIxComponent */