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