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+Scheduler.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"];
46 timeZone = [[context activeUser] timeZone];
49 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
51 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
53 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
54 privateAptTooltipFormatter
55 = [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
56 [self configureFormatters];
57 componentsData = [NSMutableDictionary new];
65 [componentsData release];
66 [appointments release];
68 [appointment release];
70 [aptFormatter release];
71 [aptTooltipFormatter release];
72 [privateAptFormatter release];
73 [privateAptTooltipFormatter release];
78 /* subclasses should override this */
79 - (void) configureFormatters
83 [aptFormatter setFullDetails];
84 [aptTooltipFormatter setTooltip];
85 [privateAptFormatter setPrivateDetails];
86 [privateAptTooltipFormatter setPrivateTooltip];
88 title = [self labelForKey: @"empty title"];
89 [aptFormatter setTitlePlaceholder: title];
90 [aptTooltipFormatter setTitlePlaceholder: title];
92 title = [self labelForKey: @"private appointment"];
93 [privateAptFormatter setPrivateTitle: title];
94 [privateAptTooltipFormatter setPrivateTitle: title];
97 - (NSArray *) filterAppointments:(NSArray *) _apts
99 NSMutableArray *filtered;
106 NSString *partmailsString;
111 if ([self shouldDisplayRejectedAppointments])
114 count = [_apts count];
115 filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
116 email = [[context activeUser] primaryEmail];
118 for (i = 0; i < count; i++)
121 info = [_apts objectAtIndex: i];
122 partmailsString = [info objectForKey: @"partmails"];
123 if ([partmailsString isNotNull])
125 partmails = [partmailsString componentsSeparatedByString: @"\n"];
126 pCount = [partmails count];
127 for (p = 0; p < pCount; p++)
129 pEmail = [partmails objectAtIndex: p];
130 if ([pEmail isEqualToString: email])
132 partstates = [[info objectForKey: @"partstates"]
133 componentsSeparatedByString: @"\n"];
134 state = [partstates objectAtIndex: p];
135 if ([state intValue] == iCalPersonPartStatDeclined)
142 [filtered addObject: info];
151 - (void) setAppointments:(NSArray *) _apts
153 _apts = [self filterAppointments: _apts];
154 ASSIGN(appointments, _apts);
157 - (NSArray *) appointments
162 - (void) setAppointment:(id) _apt
164 ASSIGN (appointment, _apt);
167 // - (void) setAppointment:(id) _apt
169 // NSString *mailtoChunk;
170 // NSString *myEmail;
171 // NSString *partmails;
173 // ASSIGN(appointment, _apt);
175 // /* cache some info about apt for faster access */
177 // mailtoChunk = [_apt valueForKey: @"orgmail"];
178 // myEmail = [self emailForUser];
179 // if ([mailtoChunk rangeOfString: myEmail].length > 0)
181 // aptFlags.isMyApt = YES;
182 // aptFlags.canAccessApt = YES;
186 // aptFlags.isMyApt = NO;
188 // partmails = [_apt valueForKey: @"partmails"];
189 // if ([partmails rangeOfString: myEmail].length)
190 // aptFlags.canAccessApt = YES;
192 // aptFlags.canAccessApt
193 // = ([[_apt valueForKey: @"classification"] intValue]
194 // == iCalAccessPublic);
205 return aptFlags.isMyApt ? YES : NO;
208 - (BOOL) canAccessApt
210 return aptFlags.canAccessApt ? YES : NO;
213 - (BOOL) canNotAccessApt
215 return aptFlags.canAccessApt ? NO : YES;
218 - (NSDictionary *) aptTypeDict
222 - (NSString *) aptTypeLabel
226 - (NSString *) aptTypeIcon
231 - (SOGoAptFormatter *) aptFormatter
233 if (aptFlags.canAccessApt)
235 return privateAptFormatter;
238 - (SOGoAptFormatter *) aptTooltipFormatter
240 if (aptFlags.canAccessApt)
241 return aptTooltipFormatter;
242 return privateAptTooltipFormatter;
245 - (void) setTasks: (NSArray *) _tasks
247 ASSIGN(tasks, _tasks);
255 /* TODO: remove this */
256 - (NSString *) shortTextForApt
258 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
259 if (![self canAccessApt])
261 return [[self aptFormatter] stringForObjectValue: appointment];
264 - (NSString *) shortTitleForApt
268 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
270 if (![self canAccessApt])
272 title = [appointment valueForKey: @"title"];
273 if ([title length] > 12)
274 title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
279 - (NSString *) tooltipForApt
281 [self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
282 return [[self aptTooltipFormatter] stringForObjectValue: appointment
283 referenceDate: [self currentDay]];
286 - (NSString *) aptStyle
291 - (NSCalendarDate *) referenceDateForFormatter
293 return [self selectedDate];
296 - (NSCalendarDate *) thisMonth
298 return [self selectedDate];
301 - (NSCalendarDate *) nextMonth
303 NSCalendarDate *date = [self thisMonth];
304 return [date dateByAddingYears: 0 months: 1 days: 0
305 hours: 0 minutes: 0 seconds: 0];
308 - (NSCalendarDate *) prevMonth
310 NSCalendarDate *date = [self thisMonth];
311 return [date dateByAddingYears: 0 months:-1 days: 0
312 hours: 0 minutes: 0 seconds: 0];
315 - (NSString *) prevMonthAsString
317 return [self dateStringForDate: [self prevMonth]];
320 - (NSString *) nextMonthAsString
322 return [self dateStringForDate: [self nextMonth]];
325 /* current day related */
327 - (void) setCurrentDay:(NSCalendarDate *) _day
329 [_day setTimeZone: timeZone];
330 ASSIGN (currentDay, _day);
333 - (NSCalendarDate *) currentDay
338 - (NSString *) currentDayName
340 return [self localizedNameForDayOfWeek: [currentDay dayOfWeek]];
348 - (NSArray *) allDayApts
351 NSMutableArray *filtered;
357 apts = [self appointments];
358 count = [apts count];
359 filtered = [[NSMutableArray alloc] initWithCapacity: 3];
360 for (i = 0; i < count; i++)
365 apt = [apts objectAtIndex: i];
366 bv = [apt valueForKey: @"isallday"];
368 [filtered addObject: apt];
371 ASSIGN(allDayApts, filtered);
377 /* special appointments */
381 return [self hasHoldidayInfo] || [self hasAllDayApts];
384 - (BOOL) hasHoldidayInfo
386 return [self holidayInfo] != nil;
389 - (BOOL) hasAllDayApts
391 return [[self allDayApts] count] != 0;
397 - (BOOL) showFullNames
402 - (BOOL) showAMPMDates
407 - (unsigned) dayStartHour
412 - (unsigned) dayEndHour
417 - (BOOL) shouldDisplayWeekend
419 return shouldDisplayWeekend;
422 - (BOOL) shouldHideWeekend
424 return ![self shouldDisplayWeekend];
430 - (NSString *) appointmentViewURL
434 if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
437 return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
438 inContext: [self context]]
439 stringByAppendingString: @"/view"];
444 - (NSCalendarDate *) startDate
446 return [self selectedDate];
449 - (NSCalendarDate *) endDate
451 return [[self startDate] tomorrow];
454 - (SOGoAppointmentFolder *) _aptFolder: (NSString *) folder
455 withClientObject: (SOGoAppointmentFolder *) clientObject
457 SOGoAppointmentFolder *aptFolder;
458 NSArray *folderParts;
460 if ([folder isEqualToString: @"/"])
461 aptFolder = clientObject;
464 folderParts = [folder componentsSeparatedByString: @":"];
466 = [clientObject lookupCalendarFolderForUID:
467 [folderParts objectAtIndex: 0]];
473 - (NSArray *) _activeCalendarFolders
475 NSMutableArray *activeFolders;
476 NSEnumerator *folders;
477 NSDictionary *currentFolderDict;
478 SOGoAppointmentFolder *currentFolder, *clientObject;
480 activeFolders = [NSMutableArray new];
481 [activeFolders autorelease];
483 clientObject = [self clientObject];
485 folders = [[clientObject calendarFolders] objectEnumerator];
486 currentFolderDict = [folders nextObject];
487 while (currentFolderDict)
489 if ([[currentFolderDict objectForKey: @"active"] boolValue])
492 = [self _aptFolder: [currentFolderDict objectForKey: @"folder"]
493 withClientObject: clientObject];
494 [activeFolders addObject: currentFolder];
497 currentFolderDict = [folders nextObject];
500 return activeFolders;
503 - (void) _updatePrivacyInObjects: (NSArray *) objectInfos
504 fromFolder: (SOGoAppointmentFolder *) folder
506 int hideDetails[] = {-1, -1, -1};
507 NSMutableDictionary *currentRecord;
509 NSString *roleString, *userLogin;
512 userLogin = [[context activeUser] login];
513 infos = [objectInfos objectEnumerator];
514 currentRecord = [infos nextObject];
515 while (currentRecord)
517 privacyFlag = [[currentRecord objectForKey: @"classification"] intValue];
518 if (hideDetails[privacyFlag] == -1)
520 roleString = [folder roleForComponentsWithAccessClass: privacyFlag
522 hideDetails[privacyFlag] = ([roleString isEqualToString: @"ComponentDAndTViewer"]
525 if (hideDetails[privacyFlag])
527 [currentRecord setObject: [self labelForKey: @"(Private Event)"]
529 [currentRecord setObject: @"" forKey: @"location"];
531 currentRecord = [infos nextObject];
535 - (NSArray *) _fetchCoreInfosForComponent: (NSString *) component
537 NSArray *currentInfos;
538 NSMutableArray *infos;
539 NSEnumerator *folders;
540 SOGoAppointmentFolder *currentFolder;
542 infos = [componentsData objectForKey: component];
545 infos = [NSMutableArray array];
546 folders = [[self _activeCalendarFolders] objectEnumerator];
547 currentFolder = [folders nextObject];
548 while (currentFolder)
550 currentInfos = [currentFolder fetchCoreInfosFrom: [[self startDate] beginOfDay]
551 to: [[self endDate] endOfDay]
552 component: component];
553 [currentInfos makeObjectsPerform: @selector (setObject:forKey:)
554 withObject: [currentFolder ownerInContext: nil]
555 withObject: @"owner"];
556 [self _updatePrivacyInObjects: currentInfos
557 fromFolder: currentFolder];
558 [infos addObjectsFromArray: currentInfos];
559 currentFolder = [folders nextObject];
561 [componentsData setObject: infos forKey: component];
567 - (NSArray *) fetchCoreAppointmentsInfos
570 [self setAppointments: [self _fetchCoreInfosForComponent: @"vevent"]];
575 - (NSArray *) fetchCoreTasksInfos
578 [self setTasks: [self _fetchCoreInfosForComponent: @"vtodo"]];
583 /* query parameters */
585 - (BOOL) shouldDisplayRejectedAppointments
589 bv = [self queryParameterForKey: @"dr"];
591 return [bv boolValue];
594 - (NSDictionary *) toggleShowRejectedAptsQueryParameters
596 NSMutableDictionary *qp;
599 shouldDisplay = ![self shouldDisplayRejectedAppointments];
600 qp = [[[self queryParameters] mutableCopy] autorelease];
601 [qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
605 - (NSString *) toggleShowRejectedAptsLabel
607 if (![self shouldDisplayRejectedAppointments])
608 return @"show_rejected_apts";
609 return @"hide_rejected_apts";
612 /* date selection & conversion */
614 - (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
616 NSCalendarDate *date;
618 date = [[self startDate] dateByAddingYears: 0 months: 0
620 hours: 0 minutes: 0 seconds: 0];
621 return [self queryParametersBySettingSelectedDate: date];
624 - (NSDictionary *) todayQueryParameters
626 return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
629 - (NSDictionary *) currentDayQueryParameters
631 return [self queryParametersBySettingSelectedDate: currentDay];
636 - (NSString *) _userFolderURI
642 ctx = [self context];
643 obj = [[ctx objectTraversalStack] objectAtIndex: 1];
644 url = [NSURL URLWithString: [obj baseURLInContext: ctx]];
645 return [[url path] stringByUnescapingURL];
648 - (id) redirectForUIDsAction
650 NSMutableString *uri;
651 NSString *uidsString, *loc, *prevMethod, *userFolderID;
652 id <WOActionResults> r;
656 uidsString = [self queryParameterForKey: @"userUIDString"];
657 uidsString = [uidsString stringByTrimmingSpaces];
658 [self setQueryParameter: nil forKey: @"userUIDString"];
660 prevMethod = [self queryParameterForKey: @"previousMethod"];
661 if (prevMethod == nil)
664 uri = [[NSMutableString alloc] initWithString: [self _userFolderURI]];
665 /* if we have more than one entry, use groups - otherwise don't */
666 useGroups = [uidsString rangeOfString: @","].length > 0;
667 userFolderID = [uri lastPathComponent];
672 uids = [uidsString componentsSeparatedByString: @","];
673 /* guarantee that our id is the first */
674 if (((index = [uids indexOfObject: userFolderID]) != NSNotFound)
677 uids = [[uids mutableCopy] autorelease];
678 [(NSMutableArray *) uids removeObjectAtIndex: index];
679 [(NSMutableArray *) uids insertObject: userFolderID atIndex: 0];
680 uidsString = [uids componentsJoinedByString: @","];
682 [uri appendString: @"Groups/_custom_"];
683 [uri appendString: uidsString];
684 [uri appendString: @"/"];
685 NSLog (@"Group URI = '%@'", uri);
689 /* check if lastPathComponent is the base that we want to have */
690 if ((([uidsString length] != 0)
691 && (![userFolderID isEqualToString: uidsString])))
695 /* uri ends with an '/', so we have to adjust the range a little */
696 r = NSMakeRange(0, [uri length] - 1);
697 r = [uri rangeOfString: @"/"
698 options: NSBackwardsSearch
700 r = NSMakeRange(r.location + 1, [uri length] - r.location - 2);
701 [uri replaceCharactersInRange: r withString: uidsString];
704 [uri appendString: @"Calendar/"];
705 [uri appendString: prevMethod];
708 NSLog(@"%s redirect uri:%@",
712 loc = [self completeHrefForMethod: uri]; /* this might return uri! */
713 r = [self redirectToLocation: loc];
718 @end /* UIxCalView */