sogo/UI/Scheduler/UIxCalView.m

758 lines
17 KiB
Mathematica
Raw Normal View History

// $Id: UIxCalView.m 1031 2007-03-07 22:52:32Z wolfgang $
#import "common.h"
//#import <OGoContentStore/OCSFolder.h>
#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/SoUser.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGCards/NGCards.h>
#import <SOGoUI/SOGoAptFormatter.h>
#import "UIxComponent+Agenor.h"
#import "SoObjects/Appointments/SOGoAppointmentFolder.h"
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoObject.h>
#import "UIxCalView.h"
@interface UIxCalView (PrivateAPI)
- (NSString *) _userFolderURI;
@end
@implementation UIxCalView
static BOOL shouldDisplayWeekend = NO;
+ (void) initialize
{
static BOOL didInit = NO;
NSUserDefaults *ud;
if (didInit) return;
ud = [NSUserDefaults standardUserDefaults];
shouldDisplayWeekend = [ud boolForKey: @"SOGoShouldDisplayWeekend"];
didInit = YES;
}
- (id) init
{
self = [super init];
if (self)
{
NSTimeZone *tz;
tz = [[self clientObject] userTimeZone];
aptFormatter
= [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
aptTooltipFormatter
= [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
privateAptFormatter
= [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
privateAptTooltipFormatter
= [[SOGoAptFormatter alloc] initWithDisplayTimeZone: tz];
[self configureFormatters];
componentsData = [NSMutableDictionary new];
calendarFolders = nil;
}
return self;
}
- (void) dealloc
{
[calendarFolders release];
[componentsData release];
[appointments release];
[allDayApts release];
[appointment release];
[currentDay release];
[aptFormatter release];
[aptTooltipFormatter release];
[privateAptFormatter release];
[privateAptTooltipFormatter release];
[super dealloc];
}
/* subclasses should override this */
- (void) configureFormatters
{
NSString *title;
[aptFormatter setFullDetails];
[aptTooltipFormatter setTooltip];
[privateAptFormatter setPrivateDetails];
[privateAptTooltipFormatter setPrivateTooltip];
title = [self labelForKey: @"empty title"];
[aptFormatter setTitlePlaceholder: title];
[aptTooltipFormatter setTitlePlaceholder: title];
title = [self labelForKey: @"private appointment"];
[privateAptFormatter setPrivateTitle: title];
[privateAptTooltipFormatter setPrivateTitle: title];
}
- (NSArray *) filterAppointments:(NSArray *) _apts
{
NSMutableArray *filtered;
unsigned i, count;
NSString *email;
NSDictionary *info;
NSArray *partmails;
unsigned p, pCount;
BOOL shouldAdd;
NSString *partmailsString;
NSArray *partstates;
NSString *state;
NSString *pEmail;
if ([self shouldDisplayRejectedAppointments])
return _apts;
{
count = [_apts count];
filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
email = [self emailForUser];
for (i = 0; i < count; i++)
{
shouldAdd = YES;
info = [_apts objectAtIndex: i];
partmailsString = [info objectForKey: @"partmails"];
if ([partmailsString isNotNull])
{
partmails = [partmailsString componentsSeparatedByString: @"\n"];
pCount = [partmails count];
for (p = 0; p < pCount; p++)
{
pEmail = [partmails objectAtIndex: p];
if ([pEmail isEqualToString: email])
{
partstates = [[info objectForKey: @"partstates"]
componentsSeparatedByString: @"\n"];
state = [partstates objectAtIndex: p];
if ([state intValue] == iCalPersonPartStatDeclined)
shouldAdd = NO;
break;
}
}
}
if (shouldAdd)
[filtered addObject: info];
}
}
return filtered;
}
/* accessors */
- (void) setAppointments:(NSArray *) _apts
{
_apts = [self filterAppointments: _apts];
ASSIGN(appointments, _apts);
}
- (NSArray *) appointments
{
return appointments;
}
- (void) setAppointment:(id) _apt
{
NSString *mailtoChunk;
NSString *myEmail;
ASSIGN(appointment, _apt);
/* cache some info about apt for faster access */
mailtoChunk = [_apt valueForKey: @"orgmail"];
myEmail = [self emailForUser];
if ([mailtoChunk rangeOfString: myEmail].length > 0)
{
aptFlags.isMyApt = YES;
aptFlags.canAccessApt = YES;
}
else
{
NSString *partmails;
aptFlags.isMyApt = NO;
partmails = [_apt valueForKey: @"partmails"];
if ([partmails rangeOfString: myEmail].length)
aptFlags.canAccessApt = YES;
else
aptFlags.canAccessApt
= ([[_apt valueForKey: @"classification"] intValue]
== iCalAccessPublic);
}
}
- (void) setTasks: (NSArray *) _tasks
{
ASSIGN(tasks, _tasks);
}
- (NSArray *) tasks
{
return tasks;
}
- (id) appointment
{
return appointment;
}
- (BOOL) isMyApt
{
return aptFlags.isMyApt ? YES : NO;
}
- (BOOL) canAccessApt
{
return aptFlags.canAccessApt ? YES : NO;
}
- (BOOL) canNotAccessApt
{
return aptFlags.canAccessApt ? NO : YES;
}
- (NSDictionary *) aptTypeDict
{
return nil;
}
- (NSString *) aptTypeLabel
{
return @"aptLabel";
}
- (NSString *) aptTypeIcon
{
return @"";
}
- (SOGoAptFormatter *) aptFormatter
{
if (aptFlags.canAccessApt)
return aptFormatter;
return privateAptFormatter;
}
- (SOGoAptFormatter *) aptTooltipFormatter
{
if (aptFlags.canAccessApt)
return aptTooltipFormatter;
return privateAptTooltipFormatter;
}
/* TODO: remove this */
- (NSString *) shortTextForApt
{
[self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
if (![self canAccessApt])
return @"";
return [[self aptFormatter] stringForObjectValue: appointment];
}
- (NSString *) shortTitleForApt
{
NSString *title;
[self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
if (![self canAccessApt])
return @"";
title = [appointment valueForKey: @"title"];
if ([title length] > 12)
title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
return title;
}
- (NSString *) tooltipForApt
{
[self warnWithFormat: @"%s IS DEPRECATED!", __PRETTY_FUNCTION__];
return [[self aptTooltipFormatter] stringForObjectValue: appointment
referenceDate: [self currentDay]];
}
- (NSString *) aptStyle
{
return nil;
}
- (NSCalendarDate *) referenceDateForFormatter
{
return [self selectedDate];
}
- (NSCalendarDate *) thisMonth
{
return [self selectedDate];
}
- (NSCalendarDate *) nextMonth
{
NSCalendarDate *date = [self thisMonth];
return [date dateByAddingYears: 0 months: 1 days: 0
hours: 0 minutes: 0 seconds: 0];
}
- (NSCalendarDate *) prevMonth
{
NSCalendarDate *date = [self thisMonth];
return [date dateByAddingYears: 0 months:-1 days: 0
hours: 0 minutes: 0 seconds: 0];
}
- (NSString *) prevMonthAsString
{
return [self dateStringForDate: [self prevMonth]];
}
- (NSString *) nextMonthAsString
{
return [self dateStringForDate: [self nextMonth]];
}
/* current day related */
- (void) setCurrentDay:(NSCalendarDate *) _day
{
[_day setTimeZone: [[self clientObject] userTimeZone]];
ASSIGN(currentDay, _day);
}
- (NSCalendarDate *) currentDay
{
return currentDay;
}
- (NSString *) currentDayName
{
return [self localizedNameForDayOfWeek: [currentDay dayOfWeek]];
}
- (id) holidayInfo
{
return nil;
}
- (NSArray *) allDayApts
{
NSArray *apts;
NSMutableArray *filtered;
unsigned i, count;
if (allDayApts)
return allDayApts;
apts = [self appointments];
count = [apts count];
filtered = [[NSMutableArray alloc] initWithCapacity: 3];
for (i = 0; i < count; i++)
{
id apt;
NSNumber *bv;
apt = [apts objectAtIndex: i];
bv = [apt valueForKey: @"isallday"];
if ([bv boolValue])
[filtered addObject: apt];
}
ASSIGN(allDayApts, filtered);
[filtered release];
return allDayApts;
}
/* special appointments */
- (BOOL) hasDayInfo
{
return [self hasHoldidayInfo] || [self hasAllDayApts];
}
- (BOOL) hasHoldidayInfo
{
return [self holidayInfo] != nil;
}
- (BOOL) hasAllDayApts
{
return [[self allDayApts] count] != 0;
}
/* defaults */
- (BOOL) showFullNames
{
return YES;
}
- (BOOL) showAMPMDates
{
return NO;
}
- (unsigned) dayStartHour
{
return 0;
}
- (unsigned) dayEndHour
{
return 23;
}
- (BOOL) shouldDisplayWeekend
{
return shouldDisplayWeekend;
}
- (BOOL) shouldHideWeekend
{
return ![self shouldDisplayWeekend];
}
/* URLs */
- (NSString *) appointmentViewURL
{
id pkey;
if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
return nil;
return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
inContext: [self context]]
stringByAppendingString: @"/view"];
}
/* fetching */
- (NSCalendarDate *) startDate
{
return [self selectedDate];
}
- (NSCalendarDate *) endDate
{
return [[self startDate] tomorrow];
}
#warning We only support ONE calendar per user at this time
- (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders
{
NSEnumerator *keys;
NSString *currentKey;
NSMutableDictionary *currentCalendar;
BOOL firstShouldBeActive;
unsigned int count;
firstShouldBeActive = YES;
keys = [[subscribedFolders allKeys] objectEnumerator];
currentKey = [keys nextObject];
count = 1;
while (currentKey)
{
currentCalendar = [NSMutableDictionary new];
[currentCalendar autorelease];
[currentCalendar
setDictionary: [subscribedFolders objectForKey: currentKey]];
[currentCalendar setObject: currentKey forKey: @"folder"];
[calendarFolders addObject: currentCalendar];
if ([[currentCalendar objectForKey: @"active"] boolValue])
firstShouldBeActive = NO;
count++;
currentKey = [keys nextObject];
}
return firstShouldBeActive;
}
- (void) _setupCalendarFolders
{
NSMutableDictionary *userCalendar, *calendarDict;
SOGoUser *activeUser;
BOOL firstActive;
calendarFolders = [NSMutableArray new];
activeUser = [context activeUser];
userCalendar = [NSMutableDictionary new];
[userCalendar autorelease];
[userCalendar setObject: @"/" forKey: @"folder"];
[userCalendar setObject: [self labelForKey: @"Calendar"]
forKey: @"displayName"];
[calendarFolders addObject: userCalendar];
calendarDict = [[activeUser userSettings] objectForKey: @"Calendar"];
firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue];
firstActive = ([self _appendSubscribedFolders:
[calendarDict objectForKey: @"SubscribedFolders"]]
|| firstActive);
[userCalendar setObject: [NSNumber numberWithBool: firstActive]
forKey: @"active"];
}
- (SOGoAppointmentFolder *) _aptFolder: (NSString *) folder
withClientObject: (SOGoAppointmentFolder *) clientObject
{
SOGoAppointmentFolder *aptFolder;
NSArray *folderParts;
if ([folder isEqualToString: @"/"])
aptFolder = clientObject;
else
{
folderParts = [folder componentsSeparatedByString: @":"];
aptFolder
= [clientObject lookupCalendarFolderForUID:
[folderParts objectAtIndex: 0]];
}
return aptFolder;
}
- (NSArray *) calendarFolders
{
if (!calendarFolders)
[self _setupCalendarFolders];
return calendarFolders;
}
- (NSArray *) _activeCalendarFolders
{
NSMutableArray *activeFolders;
NSEnumerator *folders;
NSDictionary *currentFolderDict;
SOGoAppointmentFolder *currentFolder, *clientObject;
activeFolders = [NSMutableArray new];
[activeFolders autorelease];
if (!calendarFolders)
[self _setupCalendarFolders];
clientObject = [self clientObject];
folders = [calendarFolders objectEnumerator];
currentFolderDict = [folders nextObject];
while (currentFolderDict)
{
if ([[currentFolderDict objectForKey: @"active"] boolValue])
{
currentFolder
= [self _aptFolder: [currentFolderDict objectForKey: @"folder"]
withClientObject: clientObject];
[activeFolders addObject: currentFolder];
}
currentFolderDict = [folders nextObject];
}
return activeFolders;
}
- (NSArray *) _fetchCoreInfosForComponent: (NSString *) component
{
NSArray *currentInfos;
NSMutableArray *infos;
NSEnumerator *folders;
SOGoAppointmentFolder *currentFolder;
infos = [componentsData objectForKey: component];
if (!infos)
{
infos = [NSMutableArray array];
folders = [[self _activeCalendarFolders] objectEnumerator];
currentFolder = [folders nextObject];
while (currentFolder)
{
currentInfos = [currentFolder fetchCoreInfosFrom: [[self startDate] beginOfDay]
to: [[self endDate] endOfDay]
component: component];
[currentInfos makeObjectsPerform: @selector (setObject:forKey:)
withObject: [currentFolder ownerInContext: nil]
withObject: @"owner"];
[infos addObjectsFromArray: currentInfos];
currentFolder = [folders nextObject];
}
[componentsData setObject: infos forKey: component];
}
return infos;
}
- (NSArray *) fetchCoreAppointmentsInfos
{
if (!appointments)
[self setAppointments: [self _fetchCoreInfosForComponent: @"vevent"]];
return appointments;
}
- (NSArray *) fetchCoreTasksInfos
{
if (!tasks)
[self setTasks: [self _fetchCoreInfosForComponent: @"vtodo"]];
return tasks;
}
/* query parameters */
- (BOOL) shouldDisplayRejectedAppointments
{
NSString *bv;
bv = [self queryParameterForKey: @"dr"];
if (!bv) return NO;
return [bv boolValue];
}
- (NSDictionary *) toggleShowRejectedAptsQueryParameters
{
NSMutableDictionary *qp;
BOOL shouldDisplay;
shouldDisplay = ![self shouldDisplayRejectedAppointments];
qp = [[[self queryParameters] mutableCopy] autorelease];
[qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
return qp;
}
- (NSString *) toggleShowRejectedAptsLabel
{
if (![self shouldDisplayRejectedAppointments])
return @"show_rejected_apts";
return @"hide_rejected_apts";
}
/* date selection & conversion */
- (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
{
NSCalendarDate *date;
date = [[self startDate] dateByAddingYears: 0 months: 0
days: daysOffset
hours: 0 minutes: 0 seconds: 0];
return [self queryParametersBySettingSelectedDate: date];
}
- (NSDictionary *) todayQueryParameters
{
return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
}
- (NSDictionary *) currentDayQueryParameters
{
return [self queryParametersBySettingSelectedDate: currentDay];
}
/* calendarUIDs */
- (NSString *) formattedCalendarUIDs
{
return [[[self clientObject] calendarUIDs]
componentsJoinedByString: @", "];
}
/* Actions */
- (NSString *) _userFolderURI
{
WOContext *ctx;
id obj;
NSURL *url;
ctx = [self context];
obj = [[ctx objectTraversalStack] objectAtIndex: 1];
url = [NSURL URLWithString: [obj baseURLInContext: ctx]];
return [[url path] stringByUnescapingURL];
}
- (id) redirectForUIDsAction
{
NSMutableString *uri;
NSString *uidsString, *loc, *prevMethod, *userFolderID;
id <WOActionResults> r;
BOOL useGroups;
unsigned index;
uidsString = [self queryParameterForKey: @"userUIDString"];
uidsString = [uidsString stringByTrimmingSpaces];
[self setQueryParameter: nil forKey: @"userUIDString"];
prevMethod = [self queryParameterForKey: @"previousMethod"];
if (prevMethod == nil)
prevMethod = @"";
uri = [[NSMutableString alloc] initWithString: [self _userFolderURI]];
/* if we have more than one entry, use groups - otherwise don't */
useGroups = [uidsString rangeOfString: @","].length > 0;
userFolderID = [uri lastPathComponent];
if (useGroups)
{
NSArray *uids;
uids = [uidsString componentsSeparatedByString: @","];
/* guarantee that our id is the first */
if (((index = [uids indexOfObject: userFolderID]) != NSNotFound)
&& (index != 0))
{
uids = [[uids mutableCopy] autorelease];
[(NSMutableArray *) uids removeObjectAtIndex: index];
[(NSMutableArray *) uids insertObject: userFolderID atIndex: 0];
uidsString = [uids componentsJoinedByString: @","];
}
[uri appendString: @"Groups/_custom_"];
[uri appendString: uidsString];
[uri appendString: @"/"];
NSLog (@"Group URI = '%@'", uri);
}
else
{
/* check if lastPathComponent is the base that we want to have */
if ((([uidsString length] != 0)
&& (![userFolderID isEqualToString: uidsString])))
{
NSRange r;
/* uri ends with an '/', so we have to adjust the range a little */
r = NSMakeRange(0, [uri length] - 1);
r = [uri rangeOfString: @"/"
options: NSBackwardsSearch
range: r];
r = NSMakeRange(r.location + 1, [uri length] - r.location - 2);
[uri replaceCharactersInRange: r withString: uidsString];
}
}
[uri appendString: @"Calendar/"];
[uri appendString: prevMethod];
#if 0
NSLog(@"%s redirect uri:%@",
__PRETTY_FUNCTION__,
uri);
#endif
loc = [self completeHrefForMethod: uri]; /* this might return uri! */
r = [self redirectToLocation: loc];
[uri release];
return r;
}
@end /* UIxCalView */