// $Id$ #include "UIxCalView.h" #include "common.h" #include #include "SoObjects/Appointments/SOGoAppointmentFolder.h" #include #include #include #include "UIxComponent+Agenor.h" @interface UIxCalView (PrivateAPI) - (NSString *)_userFolderURI; @end @implementation UIxCalView static BOOL shouldDisplayWeekend = NO; + (void)initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; shouldDisplayWeekend = [ud boolForKey:@"SOGoShouldDisplayWeekend"]; } - (id)init { self = [super init]; if(self) { NSTimeZone *tz; tz = [self viewTimeZone]; self->aptFormatter = [[SOGoAptFormatter alloc] initWithDisplayTimeZone:tz]; self->aptTooltipFormatter = [[SOGoAptFormatter alloc] initWithDisplayTimeZone:tz]; self->privateAptFormatter = [[SOGoAptFormatter alloc] initWithDisplayTimeZone:tz]; self->privateAptTooltipFormatter = [[SOGoAptFormatter alloc] initWithDisplayTimeZone:tz]; [self configureFormatters]; } return self; } - (void)dealloc { [self->appointments release]; [self->allDayApts release]; [self->appointment release]; [self->currentDay release]; [self->aptFormatter release]; [self->aptTooltipFormatter release]; [self->privateAptFormatter release]; [self->privateAptTooltipFormatter release]; [super dealloc]; } /* subclasses should override this */ - (void)configureFormatters { NSString *title; [self->aptFormatter setFullDetails]; [self->aptTooltipFormatter setTooltip]; [self->privateAptFormatter setPrivateDetails]; [self->privateAptTooltipFormatter setPrivateTooltip]; title = [self labelForKey:@"private appointment"]; [self->privateAptFormatter setPrivateTitle:title]; [self->privateAptTooltipFormatter setPrivateTitle:title]; } /* accessors */ - (void)setAppointments:(NSArray *)_apts { ASSIGN(self->appointments, _apts); } - (NSArray *)appointments { return self->appointments; } - (void)setAppointment:(id)_apt { NSString *mailtoChunk; NSString *myEmail; ASSIGN(self->appointment, _apt); /* cache some info about apt for faster access */ mailtoChunk = [_apt valueForKey:@"orgmail"]; myEmail = [self emailForUser]; if ([mailtoChunk rangeOfString:myEmail].length > 0) { self->aptFlags.isMyApt = YES; self->aptFlags.canAccessApt = YES; } else { self->aptFlags.isMyApt = NO; self->aptFlags.canAccessApt = [[_apt valueForKey:@"ispublic"] boolValue]; } } - (id)appointment { return self->appointment; } - (BOOL)isMyApt { return self->aptFlags.isMyApt ? YES : NO; } - (BOOL)canAccessApt { return self->aptFlags.canAccessApt ? YES : NO; } - (BOOL)canNotAccessApt { return self->aptFlags.canAccessApt ? NO : YES; } - (NSDictionary *)aptTypeDict { return nil; } - (NSString *)aptTypeLabel { return @"aptLabel"; } - (NSString *)aptTypeIcon { return @""; } - (SOGoAptFormatter *)aptFormatter { if(self->aptFlags.canAccessApt) return self->aptFormatter; return self->privateAptFormatter; } - (SOGoAptFormatter *)aptTooltipFormatter { if(self->aptFlags.canAccessApt) return self->aptTooltipFormatter; return self->privateAptTooltipFormatter; } /* TODO: remove this */ - (NSString *)shortTextForApt { [self warnWithFormat:@"%s IS DEPRECATED!", __PRETTY_FUNCTION__]; if (![self canAccessApt]) return @""; return [[self aptFormatter] stringForObjectValue:self->appointment]; } - (NSString *)shortTitleForApt { NSString *title; [self warnWithFormat:@"%s IS DEPRECATED!", __PRETTY_FUNCTION__]; if (![self canAccessApt]) return @""; title = [self->appointment valueForKey:@"title"]; if ([title length] > 12) title = [[title substringToIndex:11] stringByAppendingString:@"..."]; return title; } - (NSString *)tooltipForApt { return [[self aptTooltipFormatter] stringForObjectValue:self->appointment]; } - (NSString *)aptStyle { if (![self isMyApt]) return @"apt_other"; 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]; } /* current day related */ - (void)setCurrentDay:(NSCalendarDate *)_day { [_day setTimeZone:[self viewTimeZone]]; ASSIGN(self->currentDay, _day); } - (NSCalendarDate *)currentDay { return self->currentDay; } - (NSString *)currentDayName { return [self localizedNameForDayOfWeek:[self->currentDay dayOfWeek]]; } - (id)holidayInfo { return nil; } - (NSArray *)allDayApts { NSArray *apts; NSMutableArray *filtered; unsigned i, count; if (self->allDayApts) return self->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(self->allDayApts, filtered); [filtered release]; return self->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 8; } - (unsigned)dayEndHour { return 18; } - (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"]; } /* backend */ /* resource URLs (TODO?) */ - (NSString *)resourcePath { // TODO: broken // TODO: where is this used? return @"/sogod.woa/WebServerResources/"; } - (NSString *)favIconPath { return [[self resourcePath] stringByAppendingPathComponent:@"favicon.ico"]; } - (NSString *)cssPath { NSString *path; // TODO: there should be reusable functionality for that! #warning ZideStore specific? path = @"ControlPanel/Products/ZideStoreUI/Resources/uix.css"; return [[self context] urlWithRequestHandlerKey:@"so" path:path queryString:nil]; } - (NSString *)calCSSPath { NSString *path; // TODO: there should be reusable functionality for that! #warning ZideStore specific? path = @"ControlPanel/Products/ZideStoreUI/Resources/calendar.css"; return [[self context] urlWithRequestHandlerKey:@"so" path:path queryString:nil]; } /* fetching */ - (NSCalendarDate *)startDate { return [self selectedDate]; } - (NSCalendarDate *)endDate { return [[self startDate] tomorrow]; } - (NSArray *)_fetchCoreInfosForUIDs:(NSArray *)_uids uniqueWithSet:(NSMutableSet *)_uniquer { NSMutableArray *ma; unsigned i, count; count = [_uids count]; ma = [NSMutableArray arrayWithCapacity:count * 10]; for (i = 0; i < count; i++) { SOGoAppointmentFolder *folder; NSArray *res; int j, rcount; folder = [[self clientObject] lookupGroupCalendarFolderForUIDs:_uids inContext:[self context]]; res = [folder fetchOverviewInfosFrom:[self startDate] to:[self endDate]]; /* perform uniquing */ for (j = 0, rcount = [res count]; j < rcount; j++) { NSDictionary *record; record = [res objectAtIndex:j]; if ([_uniquer containsObject:[record valueForKey:@"uid"]]) continue; [ma addObject:record]; [_uniquer addObject:[record valueForKey:@"uid"]]; } } return ma; } - (NSArray *)fetchCoreInfos { id aptFolder; NSArray *more; id uids; if (self->appointments) return self->appointments; aptFolder = [self clientObject]; self->appointments = [[aptFolder fetchOverviewInfosFrom:[self startDate] to:[self endDate]] retain]; /* ZNeK: this is dead code, isn't it? -> check */ uids = [[[[self context] request] formValueForKey:@"uids"] stringValue]; uids = [uids length] > 0 ? [uids componentsSeparatedByString:@","] : nil; if ([uids count] > 0) { NSMutableSet *availUIDs; id tmp; [self errorWithFormat:@"fetchCoreInfos called for 'uids' form value"]; /* make appointments unique, prefer the own "versions" */ tmp = [self->appointments valueForKey:@"uid"]; availUIDs = [[NSMutableSet alloc] initWithArray:tmp]; more = [self _fetchCoreInfosForUIDs:uids uniqueWithSet:availUIDs]; if (more > 0) { NSArray *tmp; tmp = self->appointments; self->appointments = [[tmp arrayByAddingObjectsFromArray:more] retain]; [tmp release]; } [availUIDs release]; } return self->appointments; } /* date selection & conversion */ - (NSDictionary *)todayQueryParameters { NSCalendarDate *date; date = [NSCalendarDate date]; /* today */ return [self queryParametersBySettingSelectedDate:date]; } - (NSDictionary *)currentDayQueryParameters { return [self queryParametersBySettingSelectedDate:self->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; WORequest *req; id r; BOOL useGroups; unsigned index; req = [[self context] request]; uidsString = [req formValueForKey:@"anaisUIDString"]; uidsString = [uidsString stringByTrimmingWhiteSpaces]; prevMethod = [req formValueForKey:@"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:@"/"]; } 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 */