]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxCalView.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1089 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+Scheduler.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       timeZone = [[context activeUser] timeZone];
47       [timeZone retain];
48       aptFormatter
49         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
50       aptTooltipFormatter
51         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
52       privateAptFormatter
53         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
54       privateAptTooltipFormatter
55         = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
56       [self configureFormatters];
57       componentsData = [NSMutableDictionary new];
58     }
59
60   return self;
61 }
62
63 - (void) dealloc
64 {
65   [componentsData release];
66   [appointments release];
67   [allDayApts release];
68   [appointment release];
69   [currentDay release];
70   [aptFormatter release];
71   [aptTooltipFormatter release];
72   [privateAptFormatter release];
73   [privateAptTooltipFormatter release];
74   [timeZone 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 = [[context activeUser] primaryEmail];
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   ASSIGN (appointment, _apt);
165 }
166
167 // - (void) setAppointment:(id) _apt
168 // {
169 //   NSString *mailtoChunk;
170 //   NSString *myEmail;
171 //   NSString *partmails;
172
173 //   ASSIGN(appointment, _apt);
174
175 //   /* cache some info about apt for faster access */
176   
177 //   mailtoChunk = [_apt valueForKey: @"orgmail"];
178 //   myEmail = [self emailForUser];
179 //   if ([mailtoChunk rangeOfString: myEmail].length > 0)
180 //     {
181 //       aptFlags.isMyApt = YES;
182 //       aptFlags.canAccessApt = YES;
183 //     }
184 //   else
185 //     {
186 //       aptFlags.isMyApt = NO;
187
188 //       partmails = [_apt valueForKey: @"partmails"];
189 //       if ([partmails rangeOfString: myEmail].length)
190 //         aptFlags.canAccessApt = YES;
191 //       else
192 //         aptFlags.canAccessApt
193 //           = ([[_apt valueForKey: @"classification"] intValue]
194 //              == iCalAccessPublic);
195 //     }
196 // }
197
198 - (id) appointment
199 {
200   return appointment;
201 }
202
203 - (BOOL) isMyApt
204 {
205   return aptFlags.isMyApt ? YES : NO;
206 }
207
208 - (BOOL) canAccessApt
209 {
210   return aptFlags.canAccessApt ? YES : NO;
211 }
212
213 - (BOOL) canNotAccessApt
214 {
215   return aptFlags.canAccessApt ? NO : YES;
216 }
217
218 - (NSDictionary *) aptTypeDict
219 {
220   return nil;
221 }
222 - (NSString *) aptTypeLabel
223 {
224   return @"aptLabel";
225 }
226 - (NSString *) aptTypeIcon
227 {
228   return @"";
229 }
230
231 - (SOGoAptFormatter *) aptFormatter
232 {
233   if (aptFlags.canAccessApt)
234     return aptFormatter;
235   return privateAptFormatter;
236 }
237
238 - (SOGoAptFormatter *) aptTooltipFormatter
239 {
240   if (aptFlags.canAccessApt)
241     return aptTooltipFormatter;
242   return privateAptTooltipFormatter;
243 }
244
245 - (void) setTasks: (NSArray *) _tasks
246 {
247   ASSIGN(tasks, _tasks);
248 }
249
250 - (NSArray *) tasks
251 {
252   return tasks;
253 }
254
255 /* TODO: remove this */
256 - (NSString *) shortTextForApt
257 {
258   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
259   if (![self canAccessApt])
260     return @"";
261   return [[self aptFormatter] stringForObjectValue: appointment];
262 }
263
264 - (NSString *) shortTitleForApt
265 {
266   NSString *title;
267
268   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
269
270   if (![self canAccessApt])
271     return @"";
272   title = [appointment valueForKey: @"title"];
273   if ([title length] > 12)
274     title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
275   
276   return title;
277 }
278
279 - (NSString *) tooltipForApt
280 {
281   [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
282   return [[self aptTooltipFormatter] stringForObjectValue: appointment
283                                      referenceDate: [self currentDay]];
284 }
285
286 - (NSString *) aptStyle
287 {
288   return nil;
289 }
290
291 - (NSCalendarDate *) referenceDateForFormatter
292 {
293   return [self selectedDate];
294 }
295
296 - (NSCalendarDate *) thisMonth
297 {
298   return [self selectedDate];
299 }
300
301 - (NSCalendarDate *) nextMonth
302 {
303   NSCalendarDate *date = [self thisMonth];
304   return [date dateByAddingYears: 0 months: 1 days: 0
305                hours: 0 minutes: 0 seconds: 0];
306 }
307
308 - (NSCalendarDate *) prevMonth
309 {
310   NSCalendarDate *date = [self thisMonth];
311   return [date dateByAddingYears: 0 months:-1 days: 0
312                hours: 0 minutes: 0 seconds: 0];
313 }
314
315 - (NSString *) prevMonthAsString
316 {
317   return [self dateStringForDate: [self prevMonth]];
318 }
319
320 - (NSString *) nextMonthAsString
321 {
322   return [self dateStringForDate: [self nextMonth]];
323 }
324
325 /* current day related */
326
327 - (void) setCurrentDay:(NSCalendarDate *) _day
328 {
329   [_day setTimeZone: timeZone];
330   ASSIGN (currentDay, _day);
331 }
332
333 - (NSCalendarDate *) currentDay
334 {
335   return currentDay;
336 }
337
338 - (NSString *) currentDayName
339 {
340   return [self localizedNameForDayOfWeek: [currentDay dayOfWeek]];
341 }
342
343 - (id) holidayInfo
344 {
345   return nil;
346 }
347
348 - (NSArray *) allDayApts
349 {
350   NSArray        *apts;
351   NSMutableArray *filtered;
352   unsigned       i, count;
353
354   if (allDayApts)
355     return allDayApts;
356
357   apts = [self appointments];
358   count = [apts count];
359   filtered = [[NSMutableArray alloc] initWithCapacity: 3];
360   for (i = 0; i < count; i++)
361     {
362       id       apt;
363       NSNumber *bv;
364
365       apt = [apts objectAtIndex: i];
366       bv = [apt valueForKey: @"isallday"];
367       if ([bv boolValue])
368         [filtered addObject: apt];
369     }
370     
371   ASSIGN(allDayApts, filtered);
372   [filtered release];
373   return allDayApts;
374 }
375
376
377 /* special appointments */
378
379 - (BOOL) hasDayInfo
380 {
381   return [self hasHoldidayInfo] || [self hasAllDayApts];
382 }
383
384 - (BOOL) hasHoldidayInfo
385 {
386   return [self holidayInfo] != nil;
387 }
388
389 - (BOOL) hasAllDayApts
390 {
391   return [[self allDayApts] count] != 0;
392 }
393
394
395 /* defaults */
396
397 - (BOOL) showFullNames
398 {
399   return YES;
400 }
401
402 - (BOOL) showAMPMDates
403 {
404   return NO;
405 }
406
407 - (unsigned) dayStartHour
408 {
409   return 0;
410 }
411
412 - (unsigned) dayEndHour
413 {
414   return 23;
415 }
416
417 - (BOOL) shouldDisplayWeekend
418 {
419   return shouldDisplayWeekend;
420 }
421
422 - (BOOL) shouldHideWeekend
423 {
424   return ![self shouldDisplayWeekend];
425 }
426
427
428 /* URLs */
429
430 - (NSString *) appointmentViewURL
431 {
432   id pkey;
433   
434   if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
435     return nil;
436   
437   return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
438                                inContext: [self context]]
439            stringByAppendingString: @"/view"];
440 }
441
442 /* fetching */
443
444 - (NSCalendarDate *) startDate
445 {
446   return [self selectedDate];
447 }
448
449 - (NSCalendarDate *) endDate
450 {
451   return [[self startDate] tomorrow];
452 }
453
454 /* query parameters */
455
456 - (BOOL) shouldDisplayRejectedAppointments
457 {
458   NSString *bv;
459   
460   bv = [self queryParameterForKey: @"dr"];
461   if (!bv) return NO;
462   return [bv boolValue];
463 }
464
465 - (NSDictionary *) toggleShowRejectedAptsQueryParameters
466 {
467   NSMutableDictionary *qp;
468   BOOL                shouldDisplay;
469   
470   shouldDisplay = ![self shouldDisplayRejectedAppointments];
471   qp = [[[self queryParameters] mutableCopy] autorelease];
472   [qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
473   return qp;
474 }
475
476 - (NSString *) toggleShowRejectedAptsLabel
477 {
478   if (![self shouldDisplayRejectedAppointments])
479     return @"show_rejected_apts";
480   return @"hide_rejected_apts";
481 }
482
483 /* date selection & conversion */
484
485 - (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
486 {
487   NSCalendarDate *date;
488   
489   date = [[self startDate] dateByAddingYears: 0 months: 0
490                            days: daysOffset
491                            hours: 0 minutes: 0 seconds: 0];
492   return [self queryParametersBySettingSelectedDate: date];
493 }
494
495 - (NSDictionary *) todayQueryParameters
496 {
497   return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
498 }
499
500 - (NSDictionary *) currentDayQueryParameters
501 {
502   return [self queryParametersBySettingSelectedDate: currentDay];
503 }
504
505 /* Actions */
506
507 - (NSString *) _userFolderURI
508 {
509   WOContext *ctx;
510   id        obj;
511   NSURL     *url;
512
513   ctx = [self context];
514   obj = [[ctx objectTraversalStack] objectAtIndex: 1];
515   url = [NSURL URLWithString: [obj baseURLInContext: ctx]];
516   return [[url path] stringByUnescapingURL];
517 }
518
519 - (id) redirectForUIDsAction
520 {
521   NSMutableString *uri;
522   NSString *uidsString, *loc, *prevMethod, *userFolderID;
523   id <WOActionResults> r;
524   BOOL useGroups;
525   unsigned index;
526
527   uidsString = [self queryParameterForKey: @"userUIDString"];
528   uidsString = [uidsString stringByTrimmingSpaces];
529   [self setQueryParameter: nil forKey: @"userUIDString"];
530
531   prevMethod = [self queryParameterForKey: @"previousMethod"];
532   if (prevMethod == nil)
533     prevMethod = @"";
534
535   uri = [[NSMutableString alloc] initWithString: [self _userFolderURI]];
536   /* if we have more than one entry, use groups - otherwise don't */
537   useGroups = [uidsString rangeOfString: @","].length > 0;
538   userFolderID = [uri lastPathComponent];
539   if (useGroups)
540     {
541       NSArray *uids;
542
543       uids = [uidsString componentsSeparatedByString: @","];
544       /* guarantee that our id is the first */
545       if (((index = [uids indexOfObject: userFolderID]) != NSNotFound)
546           && (index != 0))
547         {
548           uids = [[uids mutableCopy] autorelease];
549           [(NSMutableArray *) uids removeObjectAtIndex: index];
550           [(NSMutableArray *) uids insertObject: userFolderID atIndex: 0];
551           uidsString = [uids componentsJoinedByString: @","];
552         }
553       [uri appendString: @"Groups/_custom_"];
554       [uri appendString: uidsString];
555       [uri appendString: @"/"];
556       NSLog (@"Group URI = '%@'", uri);
557     }
558   else
559     {
560       /* check if lastPathComponent is the base that we want to have */
561       if ((([uidsString length] != 0)
562            && (![userFolderID isEqualToString: uidsString])))
563         {
564           NSRange r;
565           
566           /* uri ends with an '/', so we have to adjust the range a little */
567           r = NSMakeRange(0, [uri length] - 1);
568           r = [uri rangeOfString: @"/"
569                    options: NSBackwardsSearch
570                    range: r];
571           r = NSMakeRange(r.location + 1, [uri length] - r.location - 2);
572           [uri replaceCharactersInRange: r withString: uidsString];
573         }
574     }
575   [uri appendString: @"Calendar/"];
576   [uri appendString: prevMethod];
577
578 #if 0
579   NSLog(@"%s redirect uri:%@",
580         __PRETTY_FUNCTION__,
581         uri);
582 #endif
583   loc = [self completeHrefForMethod: uri]; /* this might return uri! */
584   r = [self redirectToLocation: loc];
585   [uri release];
586   return r;
587 }
588
589 @end /* UIxCalView */