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