4 //#import <OGoContentStore/OCSFolder.h>
6 #import <NGObjWeb/SoSecurityManager.h>
7 #import <NGObjWeb/SoUser.h>
8 #import <NGExtensions/NGCalendarDateRange.h>
9 #import <NGCards/NGCards.h>
11 #import <SOGoUI/SOGoAptFormatter.h>
12 #import "UIxComponent+Agenor.h"
14 #import "SoObjects/Appointments/SOGoAppointmentFolder.h"
15 #import <SOGo/NSArray+Utilities.h>
16 #import <SOGo/SOGoUser.h>
17 #import <SOGo/SOGoObject.h>
19 #import "UIxCalView.h"
21 @interface UIxCalView (PrivateAPI)
22 - (NSString *) _userFolderURI;
25 @implementation UIxCalView
27 static BOOL shouldDisplayWeekend = NO;
31 static BOOL didInit = NO;
36 ud = [NSUserDefaults standardUserDefaults];
37 shouldDisplayWeekend = [ud boolForKey: @"SOGoShouldDisplayWeekend"];
48 tz = [[self clientObject] userTimeZone];
50 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
52 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
54 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
55 privateAptTooltipFormatter
56 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
57 [self configureFormatters];
58 componentsData = [NSMutableDictionary new];
59 calendarFolders = nil;
67 [calendarFolders release];
68 [componentsData release];
69 [appointments release];
71 [appointment release];
73 [aptFormatter release];
74 [aptTooltipFormatter release];
75 [privateAptFormatter release];
76 [privateAptTooltipFormatter release];
80 /* subclasses should override this */
81 - (void) configureFormatters
85 [aptFormatter setFullDetails];
86 [aptTooltipFormatter setTooltip];
87 [privateAptFormatter setPrivateDetails];
88 [privateAptTooltipFormatter setPrivateTooltip];
90 title = [self labelForKey: @"empty title"];
91 [aptFormatter setTitlePlaceholder: title];
92 [aptTooltipFormatter setTitlePlaceholder: title];
94 title = [self labelForKey: @"private appointment"];
95 [privateAptFormatter setPrivateTitle: title];
96 [privateAptTooltipFormatter setPrivateTitle: title];
99 - (NSArray *) filterAppointments:(NSArray *) _apts
101 NSMutableArray *filtered;
108 NSString *partmailsString;
113 if ([self shouldDisplayRejectedAppointments])
116 count = [_apts count];
117 filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
118 email = [self emailForUser];
120 for (i = 0; i < count; i++)
123 info = [_apts objectAtIndex: i];
124 partmailsString = [info objectForKey: @"partmails"];
125 if ([partmailsString isNotNull])
127 partmails = [partmailsString componentsSeparatedByString: @"\n"];
128 pCount = [partmails count];
129 for (p = 0; p < pCount; p++)
131 pEmail = [partmails objectAtIndex: p];
132 if ([pEmail isEqualToString: email])
134 partstates = [[info objectForKey: @"partstates"]
135 componentsSeparatedByString: @"\n"];
136 state = [partstates objectAtIndex: p];
137 if ([state intValue] == iCalPersonPartStatDeclined)
144 [filtered addObject: info];
153 - (void) setAppointments:(NSArray *) _apts
155 _apts = [self filterAppointments: _apts];
156 ASSIGN(appointments, _apts);
159 - (NSArray *) appointments
164 - (void) setAppointment:(id) _apt
166 NSString *mailtoChunk;
169 ASSIGN(appointment, _apt);
171 /* cache some info about apt for faster access */
173 mailtoChunk = [_apt valueForKey: @"orgmail"];
174 myEmail = [self emailForUser];
175 if ([mailtoChunk rangeOfString: myEmail].length > 0)
177 aptFlags.isMyApt = YES;
178 aptFlags.canAccessApt = YES;
184 aptFlags.isMyApt = NO;
186 partmails = [_apt valueForKey: @"partmails"];
187 if ([partmails rangeOfString: myEmail].length)
188 aptFlags.canAccessApt = YES;
190 aptFlags.canAccessApt
191 = ([[_apt valueForKey: @"classification"] intValue]
192 == iCalAccessPublic);
196 - (void) setTasks: (NSArray *) _tasks
198 ASSIGN(tasks, _tasks);
213 return aptFlags.isMyApt ? YES : NO;
216 - (BOOL) canAccessApt
218 return aptFlags.canAccessApt ? YES : NO;
221 - (BOOL) canNotAccessApt
223 return aptFlags.canAccessApt ? NO : YES;
226 - (NSDictionary *) aptTypeDict
230 - (NSString *) aptTypeLabel
234 - (NSString *) aptTypeIcon
239 - (SOGoAptFormatter *) aptFormatter
241 if (aptFlags.canAccessApt)
243 return privateAptFormatter;
246 - (SOGoAptFormatter *) aptTooltipFormatter
248 if (aptFlags.canAccessApt)
249 return aptTooltipFormatter;
250 return privateAptTooltipFormatter;
253 /* TODO: remove this */
254 - (NSString *) shortTextForApt
256 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
257 if (![self canAccessApt])
259 return [[self aptFormatter] stringForObjectValue: appointment];
262 - (NSString *) shortTitleForApt
266 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
268 if (![self canAccessApt])
270 title = [appointment valueForKey: @"title"];
271 if ([title length] > 12)
272 title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
277 - (NSString *) tooltipForApt
279 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
280 return [[self aptTooltipFormatter] stringForObjectValue: appointment
281 referenceDate: [self currentDay]];
284 - (NSString *) aptStyle
289 - (NSCalendarDate *) referenceDateForFormatter
291 return [self selectedDate];
294 - (NSCalendarDate *) thisMonth
296 return [self selectedDate];
299 - (NSCalendarDate *) nextMonth
301 NSCalendarDate *date = [self thisMonth];
302 return [date dateByAddingYears: 0 months: 1 days: 0
303 hours: 0 minutes: 0 seconds: 0];
306 - (NSCalendarDate *) prevMonth
308 NSCalendarDate *date = [self thisMonth];
309 return [date dateByAddingYears: 0 months:-1 days: 0
310 hours: 0 minutes: 0 seconds: 0];
313 - (NSString *) prevMonthAsString
315 return [self dateStringForDate: [self prevMonth]];
318 - (NSString *) nextMonthAsString
320 return [self dateStringForDate: [self nextMonth]];
323 /* current day related */
325 - (void) setCurrentDay:(NSCalendarDate *) _day
327 [_day setTimeZone: [[self clientObject] userTimeZone]];
328 ASSIGN(currentDay, _day);
331 - (NSCalendarDate *) currentDay
336 - (NSString *) currentDayName
338 return [self localizedNameForDayOfWeek: [currentDay dayOfWeek]];
346 - (NSArray *) allDayApts
349 NSMutableArray *filtered;
355 apts = [self appointments];
356 count = [apts count];
357 filtered = [[NSMutableArray alloc] initWithCapacity: 3];
358 for (i = 0; i < count; i++)
363 apt = [apts objectAtIndex: i];
364 bv = [apt valueForKey: @"isallday"];
366 [filtered addObject: apt];
369 ASSIGN(allDayApts, filtered);
375 /* special appointments */
379 return [self hasHoldidayInfo] || [self hasAllDayApts];
382 - (BOOL) hasHoldidayInfo
384 return [self holidayInfo] != nil;
387 - (BOOL) hasAllDayApts
389 return [[self allDayApts] count] != 0;
395 - (BOOL) showFullNames
400 - (BOOL) showAMPMDates
405 - (unsigned) dayStartHour
410 - (unsigned) dayEndHour
415 - (BOOL) shouldDisplayWeekend
417 return shouldDisplayWeekend;
420 - (BOOL) shouldHideWeekend
422 return ![self shouldDisplayWeekend];
428 - (NSString *) appointmentViewURL
432 if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
435 return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
436 inContext: [self context]]
437 stringByAppendingString: @"/view"];
442 - (NSCalendarDate *) startDate
444 return [self selectedDate];
447 - (NSCalendarDate *) endDate
449 return [[self startDate] tomorrow];
452 #warning We only support ONE calendar per user at this time
453 - (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders
456 NSString *currentKey;
457 NSMutableDictionary *currentCalendar;
458 BOOL firstShouldBeActive;
461 firstShouldBeActive = YES;
463 keys = [[subscribedFolders allKeys] objectEnumerator];
464 currentKey = [keys nextObject];
468 currentCalendar = [NSMutableDictionary new];
469 [currentCalendar autorelease];
471 setDictionary: [subscribedFolders objectForKey: currentKey]];
472 [currentCalendar setObject: currentKey forKey: @"folder"];
473 [calendarFolders addObject: currentCalendar];
474 if ([[currentCalendar objectForKey: @"active"] boolValue])
475 firstShouldBeActive = NO;
477 currentKey = [keys nextObject];
480 return firstShouldBeActive;
483 - (void) _setupCalendarFolders
485 NSMutableDictionary *userCalendar, *calendarDict;
486 SOGoUser *activeUser;
489 calendarFolders = [NSMutableArray new];
490 activeUser = [context activeUser];
492 userCalendar = [NSMutableDictionary new];
493 [userCalendar autorelease];
494 [userCalendar setObject: @"/" forKey: @"folder"];
495 [userCalendar setObject: [self labelForKey: @"Calendar"]
496 forKey: @"displayName"];
497 [calendarFolders addObject: userCalendar];
499 calendarDict = [[activeUser userSettings] objectForKey: @"Calendar"];
500 firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue];
501 firstActive = ([self _appendSubscribedFolders:
502 [calendarDict objectForKey: @"SubscribedFolders"]]
504 [userCalendar setObject: [NSNumber numberWithBool: firstActive]
508 - (SOGoAppointmentFolder *) _aptFolder: (NSString *) folder
509 withClientObject: (SOGoAppointmentFolder *) clientObject
511 SOGoAppointmentFolder *aptFolder;
512 NSArray *folderParts;
514 if ([folder isEqualToString: @"/"])
515 aptFolder = clientObject;
518 folderParts = [folder componentsSeparatedByString: @":"];
520 = [clientObject lookupCalendarFolderForUID:
521 [folderParts objectAtIndex: 0]];
527 - (NSArray *) calendarFolders
529 if (!calendarFolders)
530 [self _setupCalendarFolders];
532 return calendarFolders;
535 - (NSArray *) _activeCalendarFolders
537 NSMutableArray *activeFolders;
538 NSEnumerator *folders;
539 NSDictionary *currentFolderDict;
540 SOGoAppointmentFolder *currentFolder, *clientObject;
542 activeFolders = [NSMutableArray new];
543 [activeFolders autorelease];
545 if (!calendarFolders)
546 [self _setupCalendarFolders];
548 clientObject = [self clientObject];
550 folders = [calendarFolders objectEnumerator];
551 currentFolderDict = [folders nextObject];
552 while (currentFolderDict)
554 if ([[currentFolderDict objectForKey: @"active"] boolValue])
557 = [self _aptFolder: [currentFolderDict objectForKey: @"folder"]
558 withClientObject: clientObject];
559 [activeFolders addObject: currentFolder];
562 currentFolderDict = [folders nextObject];
565 return activeFolders;
568 - (NSArray *) _fetchCoreInfosForComponent: (NSString *) component
570 NSArray *currentInfos;
571 NSMutableArray *infos;
572 NSEnumerator *folders;
573 SOGoAppointmentFolder *currentFolder;
575 infos = [componentsData objectForKey: component];
578 infos = [NSMutableArray array];
579 folders = [[self _activeCalendarFolders] objectEnumerator];
580 currentFolder = [folders nextObject];
581 while (currentFolder)
583 currentInfos = [currentFolder fetchCoreInfosFrom: [[self startDate] beginOfDay]
584 to: [[self endDate] endOfDay]
585 component: component];
586 [currentInfos makeObjectsPerform: @selector (setObject:forKey:)
587 withObject: [currentFolder ownerInContext: nil]
588 withObject: @"owner"];
589 [infos addObjectsFromArray: currentInfos];
590 currentFolder = [folders nextObject];
592 [componentsData setObject: infos forKey: component];
598 - (NSArray *) fetchCoreAppointmentsInfos
601 [self setAppointments: [self _fetchCoreInfosForComponent: @"vevent"]];
606 - (NSArray *) fetchCoreTasksInfos
609 [self setTasks: [self _fetchCoreInfosForComponent: @"vtodo"]];
614 /* query parameters */
616 - (BOOL) shouldDisplayRejectedAppointments
620 bv = [self queryParameterForKey: @"dr"];
622 return [bv boolValue];
625 - (NSDictionary *) toggleShowRejectedAptsQueryParameters
627 NSMutableDictionary *qp;
630 shouldDisplay = ![self shouldDisplayRejectedAppointments];
631 qp = [[[self queryParameters] mutableCopy] autorelease];
632 [qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
636 - (NSString *) toggleShowRejectedAptsLabel
638 if (![self shouldDisplayRejectedAppointments])
639 return @"show_rejected_apts";
640 return @"hide_rejected_apts";
643 /* date selection & conversion */
645 - (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
647 NSCalendarDate *date;
649 date = [[self startDate] dateByAddingYears: 0 months: 0
651 hours: 0 minutes: 0 seconds: 0];
652 return [self queryParametersBySettingSelectedDate: date];
655 - (NSDictionary *) todayQueryParameters
657 return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
660 - (NSDictionary *) currentDayQueryParameters
662 return [self queryParametersBySettingSelectedDate: currentDay];
667 - (NSString *) formattedCalendarUIDs
669 return [[[self clientObject] calendarUIDs]
670 componentsJoinedByString: @", "];
675 - (NSString *) _userFolderURI
681 ctx = [self context];
682 obj = [[ctx objectTraversalStack] objectAtIndex: 1];
683 url = [NSURL URLWithString: [obj baseURLInContext: ctx]];
684 return [[url path] stringByUnescapingURL];
687 - (id) redirectForUIDsAction
689 NSMutableString *uri;
690 NSString *uidsString, *loc, *prevMethod, *userFolderID;
691 id <WOActionResults> r;
695 uidsString = [self queryParameterForKey: @"userUIDString"];
696 uidsString = [uidsString stringByTrimmingSpaces];
697 [self setQueryParameter: nil forKey: @"userUIDString"];
699 prevMethod = [self queryParameterForKey: @"previousMethod"];
700 if (prevMethod == nil)
703 uri = [[NSMutableString alloc] initWithString: [self _userFolderURI]];
704 /* if we have more than one entry, use groups - otherwise don't */
705 useGroups = [uidsString rangeOfString: @","].length > 0;
706 userFolderID = [uri lastPathComponent];
711 uids = [uidsString componentsSeparatedByString: @","];
712 /* guarantee that our id is the first */
713 if (((index = [uids indexOfObject: userFolderID]) != NSNotFound)
716 uids = [[uids mutableCopy] autorelease];
717 [(NSMutableArray *) uids removeObjectAtIndex: index];
718 [(NSMutableArray *) uids insertObject: userFolderID atIndex: 0];
719 uidsString = [uids componentsJoinedByString: @","];
721 [uri appendString: @"Groups/_custom_"];
722 [uri appendString: uidsString];
723 [uri appendString: @"/"];
724 NSLog (@"Group URI = '%@'", uri);
728 /* check if lastPathComponent is the base that we want to have */
729 if ((([uidsString length] != 0)
730 && (![userFolderID isEqualToString: uidsString])))
734 /* uri ends with an '/', so we have to adjust the range a little */
735 r = NSMakeRange(0, [uri length] - 1);
736 r = [uri rangeOfString: @"/"
737 options: NSBackwardsSearch
739 r = NSMakeRange(r.location + 1, [uri length] - r.location - 2);
740 [uri replaceCharactersInRange: r withString: uidsString];
743 [uri appendString: @"Calendar/"];
744 [uri appendString: prevMethod];
747 NSLog(@"%s redirect uri:%@",
751 loc = [self completeHrefForMethod: uri]; /* this might return uri! */
752 r = [self redirectToLocation: loc];
757 @end /* UIxCalView */