]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxCalView.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1007 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / Scheduler / UIxCalView.m
1 // $Id$
2
3 #import "common.h"
4 //#import <OGoContentStore/OCSFolder.h>
5
6 #import <NGObjWeb/SoSecurityManager.h>
7 #import <NGObjWeb/SoUser.h>
8 #import <NGExtensions/NGCalendarDateRange.h>
9 #import <NGCards/NGCards.h>
10
11 #import <SOGoUI/SOGoAptFormatter.h>
12 #import "UIxComponent+Agenor.h"
13
14 #import "SoObjects/Appointments/SOGoAppointmentFolder.h"
15 #import <SOGo/SOGoUser.h>
16 #import <SOGo/SOGoObject.h>
17
18 #import "UIxCalView.h"
19
20 @interface UIxCalView (PrivateAPI)
21 - (NSString *) _userFolderURI;
22 @end
23
24 @implementation UIxCalView
25
26 static BOOL shouldDisplayWeekend = NO;
27
28 + (void) initialize
29 {
30   static BOOL didInit = NO;
31   NSUserDefaults *ud;
32
33   if (didInit) return;
34
35   ud = [NSUserDefaults standardUserDefaults];
36   shouldDisplayWeekend = [ud boolForKey: @"SOGoShouldDisplayWeekend"];
37   didInit = YES;
38 }
39
40 - (id) init
41 {
42   self = [super init];
43   if (self)
44     {
45       NSTimeZone *tz;
46
47       tz = [[self clientObject] userTimeZone];
48       aptFormatter
49         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
50       aptTooltipFormatter
51         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
52       privateAptFormatter
53         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
54       privateAptTooltipFormatter
55         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
56       [self configureFormatters];
57       componentsData = [NSMutableDictionary new];
58     }
59   return self;
60 }
61
62 - (void) dealloc
63 {
64   [componentsData release];
65   [appointments               release];
66   [allDayApts                 release];
67   [appointment                release];
68   [currentDay                 release];
69   [aptFormatter               release];
70   [aptTooltipFormatter        release];
71   [privateAptFormatter        release];
72   [privateAptTooltipFormatter release];
73   [super dealloc];
74 }
75
76 /* subclasses should override this */
77 - (void) configureFormatters
78 {
79   NSString *title;
80
81   [aptFormatter setFullDetails];
82   [aptTooltipFormatter setTooltip];
83   [privateAptFormatter setPrivateDetails];
84   [privateAptTooltipFormatter setPrivateTooltip];
85
86   title = [self labelForKey: @"empty title"];
87   [aptFormatter setTitlePlaceholder: title];
88   [aptTooltipFormatter setTitlePlaceholder: title];
89
90   title = [self labelForKey: @"private appointment"];
91   [privateAptFormatter setPrivateTitle: title];
92   [privateAptTooltipFormatter setPrivateTitle: title];
93 }
94
95 - (NSArray *) filterAppointments:(NSArray *) _apts
96 {
97   NSMutableArray *filtered;
98   unsigned       i, count;
99   NSString       *email;
100
101   count = [_apts count];
102   if (!count) return _apts;
103   if ([self shouldDisplayRejectedAppointments]) return _apts;
104
105   filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
106   email = [self emailForUser];
107
108   for (i = 0; i < count; i++)
109     {
110       NSDictionary *info;
111       NSArray      *partmails;
112       unsigned     p, pCount;
113       BOOL         shouldAdd;
114     
115       shouldAdd = YES;
116       info = [_apts objectAtIndex: i];
117       partmails = [[info objectForKey: @"partmails"]
118                     componentsSeparatedByString: @"\n"];
119       pCount = [partmails count];
120       for (p = 0; p < pCount; p++)
121         {
122           NSString *pEmail;
123
124           pEmail = [partmails objectAtIndex: p];
125           if ([pEmail isEqualToString: email])
126             {
127               NSArray  *partstates;
128               NSString *state;
129
130               partstates = [[info objectForKey: @"partstates"]
131                              componentsSeparatedByString: @"\n"];
132               state = [partstates objectAtIndex: p];
133               if ([state intValue] == iCalPersonPartStatDeclined)
134                 shouldAdd = NO;
135               break;
136             }
137         }
138       if (shouldAdd)
139         [filtered addObject: info];
140     }
141   return filtered;
142 }
143
144 /* accessors */
145
146 - (void) setAppointments:(NSArray *) _apts
147 {
148   _apts = [self filterAppointments: _apts];
149   ASSIGN(appointments, _apts);
150 }
151
152 - (NSArray *) appointments
153 {
154   return appointments;
155 }
156
157 - (void) setAppointment:(id) _apt
158 {
159   NSString *mailtoChunk;
160   NSString *myEmail;
161
162   ASSIGN(appointment, _apt);
163
164   /* cache some info about apt for faster access */
165   
166   mailtoChunk = [_apt valueForKey: @"orgmail"];
167   myEmail = [self emailForUser];
168   if ([mailtoChunk rangeOfString: myEmail].length > 0)
169     {
170       aptFlags.isMyApt = YES;
171       aptFlags.canAccessApt = YES;
172     }
173   else
174     {
175       NSString *partmails;
176
177       aptFlags.isMyApt = NO;
178
179       partmails = [_apt valueForKey: @"partmails"];
180       if ([partmails rangeOfString: myEmail].length)
181         aptFlags.canAccessApt = YES;
182       else
183         aptFlags.canAccessApt = [[_apt valueForKey: @"ispublic"] boolValue];
184     }
185 }
186
187 - (void) setTasks: (NSArray *) _tasks
188 {
189   ASSIGN(tasks, _tasks);
190 }
191
192 - (NSArray *) tasks
193 {
194   return tasks;
195 }
196
197 - (id) appointment
198 {
199   return appointment;
200 }
201
202 - (BOOL) isMyApt
203 {
204   return aptFlags.isMyApt ? YES : NO;
205 }
206
207 - (BOOL) canAccessApt
208 {
209   return aptFlags.canAccessApt ? YES : NO;
210 }
211
212 - (BOOL) canNotAccessApt
213 {
214   return aptFlags.canAccessApt ? NO : YES;
215 }
216
217 - (NSDictionary *) aptTypeDict
218 {
219   return nil;
220 }
221 - (NSString *) aptTypeLabel
222 {
223   return @"aptLabel";
224 }
225 - (NSString *) aptTypeIcon
226 {
227   return @"";
228 }
229
230 - (SOGoAptFormatter *) aptFormatter
231 {
232   if (aptFlags.canAccessApt)
233     return aptFormatter;
234   return privateAptFormatter;
235 }
236
237 - (SOGoAptFormatter *) aptTooltipFormatter
238 {
239   if (aptFlags.canAccessApt)
240     return aptTooltipFormatter;
241   return privateAptTooltipFormatter;
242 }
243
244 /* TODO: remove this */
245 - (NSString *) shortTextForApt
246 {
247   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
248   if (![self canAccessApt])
249     return @"";
250   return [[self aptFormatter] stringForObjectValue: appointment];
251 }
252
253 - (NSString *) shortTitleForApt
254 {
255   NSString *title;
256
257   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
258
259   if (![self canAccessApt])
260     return @"";
261   title = [appointment valueForKey: @"title"];
262   if ([title length] > 12)
263     title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
264   
265   return title;
266 }
267
268 - (NSString *) tooltipForApt
269 {
270   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
271   return [[self aptTooltipFormatter] stringForObjectValue: appointment
272                                      referenceDate: [self currentDay]];
273 }
274
275 - (NSString *) aptStyle
276 {
277   return nil;
278 }
279
280 - (NSCalendarDate *) referenceDateForFormatter
281 {
282   return [self selectedDate];
283 }
284
285 - (NSCalendarDate *) thisMonth
286 {
287   return [self selectedDate];
288 }
289
290 - (NSCalendarDate *) nextMonth
291 {
292   NSCalendarDate *date = [self thisMonth];
293   return [date dateByAddingYears: 0 months: 1 days: 0
294                hours: 0 minutes: 0 seconds: 0];
295 }
296
297 - (NSCalendarDate *) prevMonth
298 {
299   NSCalendarDate *date = [self thisMonth];
300   return [date dateByAddingYears: 0 months:-1 days: 0
301                hours: 0 minutes: 0 seconds: 0];
302 }
303
304 - (NSString *) prevMonthAsString
305 {
306   return [self dateStringForDate: [self prevMonth]];
307 }
308
309 - (NSString *) nextMonthAsString
310 {
311   return [self dateStringForDate: [self nextMonth]];
312 }
313
314 /* current day related */
315
316 - (void) setCurrentDay:(NSCalendarDate *) _day
317 {
318   [_day setTimeZone: [[self clientObject] userTimeZone]];
319   ASSIGN(currentDay, _day);
320 }
321
322 - (NSCalendarDate *) currentDay
323 {
324   return currentDay;
325 }
326
327 - (NSString *) currentDayName
328 {
329   return [self localizedNameForDayOfWeek: [currentDay dayOfWeek]];
330 }
331
332 - (id) holidayInfo
333 {
334   return nil;
335 }
336
337 - (NSArray *) allDayApts
338 {
339   NSArray        *apts;
340   NSMutableArray *filtered;
341   unsigned       i, count;
342
343   if (allDayApts)
344     return allDayApts;
345
346   apts = [self appointments];
347   count = [apts count];
348   filtered = [[NSMutableArray alloc] initWithCapacity: 3];
349   for (i = 0; i < count; i++)
350     {
351       id       apt;
352       NSNumber *bv;
353
354       apt = [apts objectAtIndex: i];
355       bv = [apt valueForKey: @"isallday"];
356       if ([bv boolValue])
357         [filtered addObject: apt];
358     }
359     
360   ASSIGN(allDayApts, filtered);
361   [filtered release];
362   return allDayApts;
363 }
364
365
366 /* special appointments */
367
368 - (BOOL) hasDayInfo
369 {
370   return [self hasHoldidayInfo] || [self hasAllDayApts];
371 }
372
373 - (BOOL) hasHoldidayInfo
374 {
375   return [self holidayInfo] != nil;
376 }
377
378 - (BOOL) hasAllDayApts
379 {
380   return [[self allDayApts] count] != 0;
381 }
382
383
384 /* defaults */
385
386 - (BOOL) showFullNames
387 {
388   return YES;
389 }
390
391 - (BOOL) showAMPMDates
392 {
393   return NO;
394 }
395
396 - (unsigned) dayStartHour
397 {
398   return 0;
399 }
400
401 - (unsigned) dayEndHour
402 {
403   return 24;
404 }
405
406 - (BOOL) shouldDisplayWeekend
407 {
408   return shouldDisplayWeekend;
409 }
410
411 - (BOOL) shouldHideWeekend
412 {
413   return ![self shouldDisplayWeekend];
414 }
415
416
417 /* URLs */
418
419 - (NSString *) appointmentViewURL
420 {
421   id pkey;
422   
423   if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
424     return nil;
425   
426   return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
427                                inContext: [self context]]
428            stringByAppendingString: @"/view"];
429 }
430
431 /* fetching */
432
433 - (NSCalendarDate *) startDate
434 {
435   return [self selectedDate];
436 }
437
438 - (NSCalendarDate *) endDate
439 {
440   return [[self startDate] tomorrow];
441 }
442
443 - (NSArray *) activeCalendarFolders
444 {
445   NSUserDefaults *ud;
446   NSEnumerator *calendarUIDs;
447   SOGoAppointmentFolder *currentFolder;
448   NSMutableArray *folders;
449   NSString *currentUID;
450   SoSecurityManager *securityManager;
451
452   securityManager = [SoSecurityManager sharedSecurityManager];
453
454   folders = [NSMutableArray array];
455   ud = [[context activeUser] userDefaults];
456   calendarUIDs = [[[ud stringForKey: @"calendaruids"]
457                     componentsSeparatedByString: @","] objectEnumerator];
458   currentUID = [calendarUIDs nextObject];
459   while (currentUID)
460     {
461       if (![currentUID hasPrefix: @"-"])
462         {
463           currentFolder = [[self clientObject] lookupCalendarFolderForUID: currentUID];
464           if (currentFolder
465               && ![securityManager validatePermission: SoPerm_AccessContentsInformation
466                                    onObject: currentFolder
467                                    inContext: context])
468             [folders addObject: currentFolder];
469         }
470       currentUID = [calendarUIDs nextObject];
471     }
472
473   return folders;
474 }
475
476 - (NSArray *) _fetchCoreInfosForComponent: (NSString *) component
477 {
478   NSArray *currentInfos;
479   NSMutableArray *infos;
480   NSEnumerator *folders;
481   SOGoAppointmentFolder *currentFolder;
482
483   infos = [componentsData objectForKey: component];
484   if (!infos)
485     {
486       infos = [NSMutableArray array];
487       folders = [[self activeCalendarFolders] objectEnumerator];
488       currentFolder = [folders nextObject];
489       while (currentFolder)
490         {
491           currentInfos = [currentFolder fetchCoreInfosFrom: [[self startDate] beginOfDay]
492                                         to: [[self endDate] endOfDay]
493                                         component: component];
494           [currentInfos makeObjectsPerform: @selector (setObject:forKey:)
495                         withObject: [currentFolder ownerInContext: nil]
496                         withObject: @"owner"];
497           [infos addObjectsFromArray: currentInfos];
498           currentFolder = [folders nextObject];
499         }
500       [componentsData setObject: infos forKey: component];
501     }
502
503   return infos;
504 }
505
506 - (NSArray *) fetchCoreAppointmentsInfos
507 {
508   if (!appointments)
509     [self setAppointments: [self _fetchCoreInfosForComponent: @"vevent"]];
510   
511   return appointments;
512 }
513
514 - (NSArray *) fetchCoreTasksInfos
515 {
516   if (!tasks)
517     [self setTasks: [self _fetchCoreInfosForComponent: @"vtodo"]];
518   
519   return tasks;
520 }
521
522 /* query parameters */
523
524 - (BOOL) shouldDisplayRejectedAppointments
525 {
526   NSString *bv;
527   
528   bv = [self queryParameterForKey: @"dr"];
529   if (!bv) return NO;
530   return [bv boolValue];
531 }
532
533 - (NSDictionary *) toggleShowRejectedAptsQueryParameters
534 {
535   NSMutableDictionary *qp;
536   BOOL                shouldDisplay;
537   
538   shouldDisplay = ![self shouldDisplayRejectedAppointments];
539   qp = [[[self queryParameters] mutableCopy] autorelease];
540   [qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
541   return qp;
542 }
543
544 - (NSString *) toggleShowRejectedAptsLabel
545 {
546   if (![self shouldDisplayRejectedAppointments])
547     return @"show_rejected_apts";
548   return @"hide_rejected_apts";
549 }
550
551 /* date selection & conversion */
552
553 - (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
554 {
555   NSCalendarDate *date;
556   
557   date = [[self startDate] dateByAddingYears: 0 months: 0
558                            days: daysOffset
559                            hours: 0 minutes: 0 seconds: 0];
560   return [self queryParametersBySettingSelectedDate: date];
561 }
562
563 - (NSDictionary *) todayQueryParameters
564 {
565   return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
566 }
567
568 - (NSDictionary *) currentDayQueryParameters
569 {
570   return [self queryParametersBySettingSelectedDate: currentDay];
571 }
572
573 /* calendarUIDs */
574
575 - (NSString *) formattedCalendarUIDs
576 {
577   return [[[self clientObject] calendarUIDs]
578            componentsJoinedByString: @", "];
579 }
580
581 /* Actions */
582
583 - (NSString *) _userFolderURI
584 {
585   WOContext *ctx;
586   id        obj;
587   NSURL     *url;
588
589   ctx = [self context];
590   obj = [[ctx objectTraversalStack] objectAtIndex: 1];
591   url = [NSURL URLWithString: [obj baseURLInContext: ctx]];
592   return [[url path] stringByUnescapingURL];
593 }
594
595 - (id) redirectForUIDsAction
596 {
597   NSMutableString *uri;
598   NSString *uidsString, *loc, *prevMethod, *userFolderID;
599   id <WOActionResults> r;
600   BOOL useGroups;
601   unsigned index;
602
603   uidsString = [self queryParameterForKey: @"userUIDString"];
604   uidsString = [uidsString stringByTrimmingWhiteSpaces];
605   [self setQueryParameter: nil forKey: @"userUIDString"];
606
607   prevMethod = [self queryParameterForKey: @"previousMethod"];
608   if (prevMethod == nil)
609     prevMethod = @"";
610
611   uri = [[NSMutableString alloc] initWithString: [self _userFolderURI]];
612   /* if we have more than one entry, use groups - otherwise don't */
613   useGroups = [uidsString rangeOfString: @","].length > 0;
614   userFolderID = [uri lastPathComponent];
615   if (useGroups)
616     {
617       NSArray *uids;
618
619       uids = [uidsString componentsSeparatedByString: @","];
620       /* guarantee that our id is the first */
621       if (((index = [uids indexOfObject: userFolderID]) != NSNotFound)
622           && (index != 0))
623         {
624           uids = [[uids mutableCopy] autorelease];
625           [(NSMutableArray *) uids removeObjectAtIndex: index];
626           [(NSMutableArray *) uids insertObject: userFolderID atIndex: 0];
627           uidsString = [uids componentsJoinedByString: @","];
628         }
629       [uri appendString: @"Groups/_custom_"];
630       [uri appendString: uidsString];
631       [uri appendString: @"/"];
632       NSLog (@"Group URI = '%@'", uri);
633     }
634   else
635     {
636       /* check if lastPathComponent is the base that we want to have */
637       if ((([uidsString length] != 0)
638            && (![userFolderID isEqualToString: uidsString])))
639         {
640           NSRange r;
641           
642           /* uri ends with an '/', so we have to adjust the range a little */
643           r = NSMakeRange(0, [uri length] - 1);
644           r = [uri rangeOfString: @"/"
645                    options: NSBackwardsSearch
646                    range: r];
647           r = NSMakeRange(r.location + 1, [uri length] - r.location - 2);
648           [uri replaceCharactersInRange: r withString: uidsString];
649         }
650     }
651   [uri appendString: @"Calendar/"];
652   [uri appendString: prevMethod];
653
654 #if 0
655   NSLog(@"%s redirect uri:%@",
656         __PRETTY_FUNCTION__,
657         uri);
658 #endif
659   loc = [self completeHrefForMethod: uri]; /* this might return uri! */
660   r = [self redirectToLocation: loc];
661   [uri release];
662   return r;
663 }
664
665 @end /* UIxCalView */