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