From 6744b1e0ea75bdeed62e4c940ab1cae24b33efe9 Mon Sep 17 00:00:00 2001 From: znek Date: Thu, 17 Feb 2005 18:33:06 +0000 Subject: [PATCH] various fixes and improvements git-svn-id: http://svn.opengroupware.org/SOPE/trunk@570 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-ical/NGiCal/ChangeLog | 17 ++++ sope-ical/NGiCal/NGiCal.xcode/project.pbxproj | 6 +- sope-ical/NGiCal/Version | 2 +- sope-ical/NGiCal/iCalEntityObject.h | 7 ++ sope-ical/NGiCal/iCalEntityObject.m | 66 +++++++++++++ sope-ical/NGiCal/iCalRecurrenceCalculator.m | 98 ++++++++++++++++++- sope-ical/NGiCal/iCalRecurrenceRule.m | 10 +- sope-ical/NGiCal/iCalRenderer.m | 93 ++++++++++++++---- sope-ical/NGiCal/iCalRepeatableEntityObject.h | 1 + sope-ical/NGiCal/iCalRepeatableEntityObject.m | 3 + 10 files changed, 275 insertions(+), 28 deletions(-) diff --git a/sope-ical/NGiCal/ChangeLog b/sope-ical/NGiCal/ChangeLog index 1ac9c871..9ecb46a7 100644 --- a/sope-ical/NGiCal/ChangeLog +++ b/sope-ical/NGiCal/ChangeLog @@ -1,3 +1,20 @@ +2005-02-17 Marcus Mueller + + * v4.5.44 + + * iCalEntityObject.[hm]: added convenience API + + * iCalRepeatableEntityObject.[hm]: added convenience API + + * iCalRecurrenceRule.m: bugfixes in -byDayList and + -iCalRepresentationForWeekDay: + + * iCalRecurrenceCalculator.m: implemented 'BYDAY' calculations for + weekly frequency. Note that 'COUNT' is still broken for this case. + + * iCalRenderer.m: updated rendering, now can render recurrence rules + and accompanied stuff properly. + 2005-02-15 Marcus Mueller * v4.5.43 diff --git a/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj b/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj index fffebc1d..260e7a10 100644 --- a/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj +++ b/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj @@ -86,7 +86,7 @@ sourceTree = ""; }; ADAACE6707B3973900FC48D6 = { - fileEncoding = 4; + fileEncoding = 5; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iCalRecurrenceCalculator.m; @@ -164,7 +164,7 @@ ); buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 4.5.43; + DYLIB_CURRENT_VERSION = 4.5.44; FRAMEWORK_SEARCH_PATHS = "\"$(USER_LIBRARY_DIR)/EmbeddedFrameworks\""; FRAMEWORK_VERSION = A; GCC_PRECOMPILE_PREFIX_HEADER = NO; @@ -578,7 +578,7 @@ ); buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 4.5.43; + DYLIB_CURRENT_VERSION = 4.5.44; FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks"; FRAMEWORK_VERSION = A; GCC_PRECOMPILE_PREFIX_HEADER = YES; diff --git a/sope-ical/NGiCal/Version b/sope-ical/NGiCal/Version index 116461db..9732ed14 100644 --- a/sope-ical/NGiCal/Version +++ b/sope-ical/NGiCal/Version @@ -2,7 +2,7 @@ MAJOR_VERSION=4 MINOR_VERSION=5 -SUBMINOR_VERSION:=43 +SUBMINOR_VERSION:=44 # v4.5.40 requires NGExtensions v4.5.145 # v4.5.37 requires NGExtensions v4.5.140 diff --git a/sope-ical/NGiCal/iCalEntityObject.h b/sope-ical/NGiCal/iCalEntityObject.h index 769230fa..64a14ea4 100644 --- a/sope-ical/NGiCal/iCalEntityObject.h +++ b/sope-ical/NGiCal/iCalEntityObject.h @@ -94,6 +94,7 @@ - (void)setOrganizer:(iCalPerson *)_organizer; - (iCalPerson *)organizer; +- (BOOL)isOrganizer:(id)_email; - (void)setStatus:(NSString *)_value; - (NSString *)status; @@ -102,6 +103,12 @@ - (void)addToAttendees:(iCalPerson *)_person; - (NSArray *)attendees; +/* categorize attendees into participants and resources */ +- (NSArray *)participants; +- (NSArray *)resources; +- (BOOL)isParticipant:(id)_email; +- (iCalPerson *)findParticipantWithEmail:(id)_email; + - (void)removeAllAlarms; - (void)addToAlarms:(id)_alarm; - (NSArray *)alarms; diff --git a/sope-ical/NGiCal/iCalEntityObject.m b/sope-ical/NGiCal/iCalEntityObject.m index 4700626b..2d45b784 100644 --- a/sope-ical/NGiCal/iCalEntityObject.m +++ b/sope-ical/NGiCal/iCalEntityObject.m @@ -23,6 +23,9 @@ #include "iCalPerson.h" #include "common.h" +@interface iCalEntityObject (PrivateAPI) +- (NSArray *)_filteredAttendeesThinkingOfPersons:(BOOL)_persons; +@end @implementation iCalEntityObject @@ -211,4 +214,67 @@ return self->alarms; } +/* stuff */ + +- (NSArray *)participants { + return [self _filteredAttendeesThinkingOfPersons:YES]; +} +- (NSArray *)resources { + return [self _filteredAttendeesThinkingOfPersons:NO]; +} + +- (NSArray *)_filteredAttendeesThinkingOfPersons:(BOOL)_persons { + NSArray *list; + NSMutableArray *filtered; + unsigned i, count; + + list = [self attendees]; + count = [list count]; + filtered = [NSMutableArray arrayWithCapacity:count]; + for (i = 0; i < count; i++) { + iCalPerson *p; + NSString *role; + + p = [list objectAtIndex:i]; + role = [p role]; + if (_persons) { + if (role == nil || ![role hasPrefix:@"NON-PART"]) + [filtered addObject:p]; + } + else { + if ([role hasPrefix:@"NON-PART"]) + [filtered addObject:p]; + } + } + return filtered; +} + +- (BOOL)isOrganizer:(id)_email { + return [[[self organizer] rfc822Email] isEqualToString:_email]; +} + +- (BOOL)isParticipant:(id)_email { + NSArray *partEmails; + + partEmails = [[self participants] valueForKey:@"rfc822Email"]; + return [partEmails containsObject:_email]; +} + +- (iCalPerson *)findParticipantWithEmail:(id)_email { + NSArray *ps; + unsigned i, count; + + ps = [self participants]; + count = [ps count]; + + for (i = 0; i < count; i++) { + iCalPerson *p; + + p = [ps objectAtIndex:i]; + if ([[p rfc822Email] isEqualToString:_email]) + return p; + } + return nil; /* not found */ +} + @end /* iCalEntityObject */ diff --git a/sope-ical/NGiCal/iCalRecurrenceCalculator.m b/sope-ical/NGiCal/iCalRecurrenceCalculator.m index 7878d45b..2931f5a6 100644 --- a/sope-ical/NGiCal/iCalRecurrenceCalculator.m +++ b/sope-ical/NGiCal/iCalRecurrenceCalculator.m @@ -51,6 +51,12 @@ @interface iCalRecurrenceCalculator (PrivateAPI) - (NSCalendarDate *)lastInstanceStartDate; + +- (unsigned)offsetFromSundayForJulianNumber:(long)_jn; +- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay; +- (unsigned)offsetFromSundayForCurrentWeekStart; + +- (iCalWeekDay)weekDayForJulianNumber:(long)_jn; @end @implementation iCalRecurrenceCalculator @@ -105,6 +111,49 @@ static Class yearlyCalcClass = Nil; return self; } +/* helpers */ + +- (unsigned)offsetFromSundayForJulianNumber:(long)_jn { + return (unsigned)((int)(_jn + 1.5)) % 7; +} + +- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay { + unsigned offset; + + switch (_weekDay) { + case iCalWeekDaySunday: offset = 0; break; + case iCalWeekDayMonday: offset = 1; break; + case iCalWeekDayTuesday: offset = 2; break; + case iCalWeekDayWednesday: offset = 3; break; + case iCalWeekDayThursday: offset = 4; break; + case iCalWeekDayFriday: offset = 5; break; + case iCalWeekDaySaturday: offset = 6; break; + default: offset = 0; break; + } + return offset; +} + +- (unsigned)offsetFromSundayForCurrentWeekStart { + return [self offsetFromSundayForWeekDay:[self->rrule weekStart]]; +} + +- (iCalWeekDay)weekDayForJulianNumber:(long)_jn { + unsigned day; + iCalWeekDay weekDay; + + day = [self offsetFromSundayForJulianNumber:_jn]; + switch (day) { + case 0: weekDay = iCalWeekDaySunday; break; + case 1: weekDay = iCalWeekDayMonday; break; + case 2: weekDay = iCalWeekDayTuesday; break; + case 3: weekDay = iCalWeekDayWednesday; break; + case 4: weekDay = iCalWeekDayThursday; break; + case 5: weekDay = iCalWeekDayFriday; break; + case 6: weekDay = iCalWeekDaySaturday; break; + default: weekDay = iCalWeekDaySunday; break; /* keep compiler happy */ + } + return weekDay; +} /* calculation */ @@ -311,7 +360,52 @@ static Class yearlyCalcClass = Nil; } } else { - [self errorWithFormat:@"Cannot calculate rules with byDayMask, yet!"]; + long jnFirstWeekStart, weekStartOffset; + + /* calculate jnFirst's week start - this depends on our setting of week + start */ + weekStartOffset = [self offsetFromSundayForJulianNumber:jnFirst] - + [self offsetFromSundayForCurrentWeekStart]; + + jnFirstWeekStart = jnFirst - weekStartOffset; + + for (i = 0 ; i < startEndCount; i++) { + long jnCurrent; + + jnCurrent = jnStart + i; + if (jnCurrent >= jnFirst) { + long jnDiff; + + /* we need to calculate a difference in weeks */ + jnDiff = (jnCurrent - jnFirstWeekStart) % 7; + if ((jnDiff % interval) == 0) { + BOOL isRecurrence = NO; + + if (jnCurrent == jnFirst) { + isRecurrence = YES; + } + else { + iCalWeekDay weekDay; + + weekDay = [self weekDayForJulianNumber:jnCurrent]; + isRecurrence = (weekDay & [self->rrule byDayMask]) ? YES : NO; + } + if (isRecurrence) { + NSCalendarDate *start, *end; + NGCalendarDateRange *r; + + start = [NSCalendarDate dateForJulianNumber:jnCurrent]; + start = [start hour: [firStart hourOfDay] + minute:[firStart minuteOfHour] + second:[firStart secondOfMinute]]; + end = [start addTimeInterval:[self->firstRange duration]]; + r = [NGCalendarDateRange calendarDateRangeWithStartDate:start + endDate:end]; + [ranges addObject:r]; + } + } + } + } } return ranges; } @@ -422,7 +516,7 @@ static Class yearlyCalcClass = Nil; ranges = [NSMutableArray arrayWithCapacity:count]; for (i = 0 ; i < count; i++) { unsigned test; - + test = diff + i; if ((test % interval) == 0) { NSCalendarDate *start, *end; diff --git a/sope-ical/NGiCal/iCalRecurrenceRule.m b/sope-ical/NGiCal/iCalRecurrenceRule.m index 4665b064..7c09525f 100644 --- a/sope-ical/NGiCal/iCalRecurrenceRule.m +++ b/sope-ical/NGiCal/iCalRecurrenceRule.m @@ -164,7 +164,7 @@ } - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay { - switch (self->byDay.weekStart) { + switch (_weekDay) { case iCalWeekDayMonday: return @"MO"; case iCalWeekDayTuesday: @@ -222,21 +222,21 @@ */ - (NSString *)byDayList { NSMutableString *s; - unsigned i, day; + unsigned i, mask, day; BOOL needsComma; s = [NSMutableString stringWithCapacity:20]; needsComma = NO; + mask = self->byDay.mask; day = iCalWeekDayMonday; - for (i = 0; i < 7; i++) { - if (self->byDay.mask && day) { + if (mask & day) { if (needsComma) [s appendString:@","]; [s appendString:[self iCalRepresentationForWeekDay:day]]; needsComma = YES; } - day = day << 1; + day = (day << 1); } return s; } diff --git a/sope-ical/NGiCal/iCalRenderer.m b/sope-ical/NGiCal/iCalRenderer.m index 35361299..c7e108af 100644 --- a/sope-ical/NGiCal/iCalRenderer.m +++ b/sope-ical/NGiCal/iCalRenderer.m @@ -22,12 +22,10 @@ #include "iCalRenderer.h" #include "iCalEvent.h" #include "iCalPerson.h" +#include "iCalRecurrenceRule.h" +#include "NSCalendarDate+ICal.h" #include "common.h" -@interface NSDate(UsedPrivates) -- (NSString *)icalString; // declared in NGiCal -@end - @implementation iCalRenderer static iCalRenderer *renderer = nil; @@ -91,8 +89,16 @@ static unsigned DefaultICalStringCapacity = 1024; [s appendString:@";"]; } + if ((x = [p partStat])) { + if ([p participationStatus] != iCalPersonPartStatNeedsAction) { + [s appendString:@"PARTSTAT="]; + [s appendString:[x iCalSafeString]]; + [s appendString:@";"]; + } + } + [s appendString:@"CN=\""]; - if ((x = [p cn])) { + if ((x = [p cnWithoutQuotes])) { [s appendString:[x iCalDQUOTESafeString]]; } [s appendString:@"\""]; @@ -165,11 +171,66 @@ static unsigned DefaultICalStringCapacity = 1024; [s appendString:@"\r\n"]; } - - /* what's all this? */ - [s appendString:@"TRANSP:OPAQUE\r\n"]; /* transparency */ - [s appendString:@"CLASS:PRIVATE\r\n"]; /* classification [like 'top secret'] */ - + if ((tmp = [event transparency]) != nil) { + [s appendString:@"TRANSP:"]; + [s appendString:tmp]; + [s appendString:@"\r\n"]; + } + + [s appendString:@"CLASS:"]; + [s appendString:[event accessClass]]; + [s appendString:@"\r\n"]; + + /* recurrence rules */ + if ([event hasRecurrenceRules]) { + NSArray *rules; + unsigned i, count; + + rules = [event recurrenceRules]; + count = [rules count]; + for (i = 0; i < count; i++) { + iCalRecurrenceRule *rule; + + rule = [rules objectAtIndex:i]; + [s appendString:@"RRULE:"]; + [s appendString:[rule iCalRepresentation]]; + [s appendString:@"\r\n"]; + } + } + + /* exception rules */ + if ([event hasExceptionRules]) { + NSArray *rules; + unsigned i, count; + + rules = [event exceptionRules]; + count = [rules count]; + for (i = 0; i < count; i++) { + iCalRecurrenceRule *rule; + + rule = [rules objectAtIndex:i]; + [s appendString:@"EXRULE:"]; + [s appendString:[rule iCalRepresentation]]; + [s appendString:@"\r\n"]; + } + } + + /* exception dates */ + if ([event hasExceptionDates]) { + NSArray *dates; + unsigned i, count; + + dates = [event exceptionDates]; + count = [dates count]; + [s appendString:@"EXDATE:"]; + for (i = 0; i < count; i++) { + if (i > 0) + [s appendString:@","]; + [s appendString:[[dates objectAtIndex:i] icalString]]; + } + [s appendString:@"\r\n"]; + } + [self addOrganizer:[event organizer] toString:s]; [self addAttendees:[event attendees] toString:s]; @@ -182,16 +243,14 @@ static unsigned DefaultICalStringCapacity = 1024; return NO; if ([[_apt uid] length] == 0) { - [self logWithFormat: - @"WARNING: got apt without uid, rejecting iCal generation: %@", - _apt]; + [self warnWithFormat:@"got apt without uid, rejecting iCal generation: %@", + _apt]; return NO; } if ([[[_apt startDate] icalString] length] == 0) { - [self logWithFormat: - @"WARNING: got apt without start date, " - @"rejecting iCal generation: %@", - _apt]; + [self warnWithFormat:@"got apt without start date, " + @"rejecting iCal generation: %@", + _apt]; return NO; } diff --git a/sope-ical/NGiCal/iCalRepeatableEntityObject.h b/sope-ical/NGiCal/iCalRepeatableEntityObject.h index a57c01a6..a8cc1c1e 100644 --- a/sope-ical/NGiCal/iCalRepeatableEntityObject.h +++ b/sope-ical/NGiCal/iCalRepeatableEntityObject.h @@ -53,6 +53,7 @@ - (void)removeAllExceptionDates; - (void)addToExceptionDates:(id)_date; +- (BOOL)hasExceptionDates; - (NSArray *)exceptionDates; - (BOOL)isRecurrent; diff --git a/sope-ical/NGiCal/iCalRepeatableEntityObject.m b/sope-ical/NGiCal/iCalRepeatableEntityObject.m index 00f9fae7..48ab00cc 100644 --- a/sope-ical/NGiCal/iCalRepeatableEntityObject.m +++ b/sope-ical/NGiCal/iCalRepeatableEntityObject.m @@ -95,6 +95,9 @@ self->exDates = [[NSMutableArray alloc] initWithCapacity:4]; [self->exDates addObject:_date]; } +- (BOOL)hasExceptionDates { + return [self->exDates count] > 0 ? YES : NO; +} - (NSArray *)exceptionDates { return self->exDates; } -- 2.39.5