From: helge Date: Tue, 19 Oct 2004 22:09:35 +0000 (+0000) Subject: added iCal renderer X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41d08ad5eb7a18343b4af6a2079cd515a9b7a57d;p=sope added iCal renderer git-svn-id: http://svn.opengroupware.org/SOPE/trunk@280 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- diff --git a/sope-ical/NGiCal/ChangeLog b/sope-ical/NGiCal/ChangeLog index 100ac049..bfcdcefe 100644 --- a/sope-ical/NGiCal/ChangeLog +++ b/sope-ical/NGiCal/ChangeLog @@ -1,3 +1,12 @@ +2004-10-20 Helge Hess + + * v4.3.32 + + * iCalEvent.m: added -vEvent method to produce an iCalendar + representation of an event + + * added iCalRenderer class based on the SOGo render by ZNeK + 2004-10-14 Marcus Mueller * NGiCal.xmap: added "categories" mapping (v4.3.31) diff --git a/sope-ical/NGiCal/GNUmakefile b/sope-ical/NGiCal/GNUmakefile index ea872bbb..cbcb84dc 100644 --- a/sope-ical/NGiCal/GNUmakefile +++ b/sope-ical/NGiCal/GNUmakefile @@ -27,6 +27,7 @@ libNGiCal_HEADER_FILES = \ iCalTrigger.h \ iCalDataSource.h \ iCalEventChanges.h \ + iCalRenderer.h \ # IcalResponse.h \ @@ -48,6 +49,7 @@ libNGiCal_OBJC_FILES = \ iCalTrigger.m \ iCalDataSource.m \ iCalEventChanges.m \ + iCalRenderer.m \ # NSString+ICal.m \ # IcalElements.m diff --git a/sope-ical/NGiCal/Version b/sope-ical/NGiCal/Version index 5c4b8ca3..d5af3010 100644 --- a/sope-ical/NGiCal/Version +++ b/sope-ical/NGiCal/Version @@ -1,5 +1,5 @@ -# $Id: Version 12 2004-08-20 19:49:55Z helge $ +# version file MAJOR_VERSION=4 MINOR_VERSION=3 -SUBMINOR_VERSION:=31 +SUBMINOR_VERSION:=32 diff --git a/sope-ical/NGiCal/iCalCalendar.h b/sope-ical/NGiCal/iCalCalendar.h index 38c67461..b53f858f 100644 --- a/sope-ical/NGiCal/iCalCalendar.h +++ b/sope-ical/NGiCal/iCalCalendar.h @@ -1,7 +1,7 @@ /* - Copyright (C) 2000-2003 SKYRIX Software AG + Copyright (C) 2000-2004 SKYRIX Software AG - This file is part of OGo + This file is part of OpenGroupware.org. OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #ifndef __NGiCal_iCalCalendar_H__ #define __NGiCal_iCalCalendar_H__ diff --git a/sope-ical/NGiCal/iCalEntityObject.h b/sope-ical/NGiCal/iCalEntityObject.h index c7c85f42..1c844b93 100644 --- a/sope-ical/NGiCal/iCalEntityObject.h +++ b/sope-ical/NGiCal/iCalEntityObject.h @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #ifndef __NGiCal_iCalEntityObject_H__ #define __NGiCal_iCalEntityObject_H__ diff --git a/sope-ical/NGiCal/iCalEntityObject.m b/sope-ical/NGiCal/iCalEntityObject.m index 905fbc68..5130417b 100644 --- a/sope-ical/NGiCal/iCalEntityObject.m +++ b/sope-ical/NGiCal/iCalEntityObject.m @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #include "iCalEntityObject.h" #include "iCalPerson.h" diff --git a/sope-ical/NGiCal/iCalEvent.h b/sope-ical/NGiCal/iCalEvent.h index 19f74fcb..b6bfa955 100644 --- a/sope-ical/NGiCal/iCalEvent.h +++ b/sope-ical/NGiCal/iCalEvent.h @@ -1,7 +1,7 @@ /* - Copyright (C) 2000-2003 SKYRIX Software AG + Copyright (C) 2000-2004 SKYRIX Software AG - This file is part of OGo + This file is part of OpenGroupware.org. OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #ifndef __NGiCal_iCalEvent_H__ #define __NGiCal_iCalEvent_H__ @@ -53,8 +52,14 @@ - (BOOL)hasDuration; - (NSTimeInterval)durationAsTimeInterval; +/* calculating changes */ + - (iCalEventChanges *)getChangesRelativeToEvent:(iCalEvent *)_event; +/* generating iCal content */ + +- (NSString *)vEventString; + @end #endif /* __NGiCal_iCalEvent_H__ */ diff --git a/sope-ical/NGiCal/iCalEvent.m b/sope-ical/NGiCal/iCalEvent.m index b88259e5..72fb3a95 100644 --- a/sope-ical/NGiCal/iCalEvent.m +++ b/sope-ical/NGiCal/iCalEvent.m @@ -18,11 +18,11 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #include "iCalEvent.h" #include "iCalPerson.h" #include "iCalEventChanges.h" +#include "iCalRenderer.h" #include "common.h" @interface NSString(DurationTimeInterval) @@ -144,6 +144,12 @@ toEvent:self]; } +/* generating iCal content */ + +- (NSString *)vEventString { + return [[iCalRenderer sharedICalendarRenderer] vEventStringForEvent:self]; +} + @end /* iCalEvent */ @implementation NSString(DurationTimeInterval) diff --git a/sope-ical/NGiCal/iCalObject.h b/sope-ical/NGiCal/iCalObject.h index 0ed2228b..2b698328 100644 --- a/sope-ical/NGiCal/iCalObject.h +++ b/sope-ical/NGiCal/iCalObject.h @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #ifndef __NGiCal_iCalObject_H__ #define __NGiCal_iCalObject_H__ diff --git a/sope-ical/NGiCal/iCalRenderer.h b/sope-ical/NGiCal/iCalRenderer.h new file mode 100644 index 00000000..e9857a57 --- /dev/null +++ b/sope-ical/NGiCal/iCalRenderer.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2004 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#ifndef __NGiCal_iCalRenderer_H_ +#define __NGiCal_iCalRenderer_H_ + +#import + +/* + iCalRenderer + + Transform an iCalEvent into an iCalendar formatted string. +*/ + +@class NSString, NSCharacterSet; +@class iCalEvent; + +@interface iCalRenderer : NSObject + ++ (id)sharedICalendarRenderer; + +- (NSString *)vEventStringForEvent:(iCalEvent *)_apt; +- (NSString *)iCalendarStringForEvent:(iCalEvent *)_apt; + +@end + +#import + +@interface NSString (SOGoiCal) + +- (NSString *)iCalDQUOTESafeString; +- (NSString *)iCalSafeString; +- (NSString *)iCalEscapedStringWithEscapeSet:(NSCharacterSet *)_es; + +@end + +#endif /* __NGiCal_iCalRenderer_H_ */ diff --git a/sope-ical/NGiCal/iCalRenderer.m b/sope-ical/NGiCal/iCalRenderer.m new file mode 100644 index 00000000..dec0e083 --- /dev/null +++ b/sope-ical/NGiCal/iCalRenderer.m @@ -0,0 +1,320 @@ +/* + Copyright (C) 2004 SKYRIX Software AG + + This file is part of OpenGroupware.org. + + OGo is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + OGo is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OGo; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "iCalRenderer.h" +#include "iCalEvent.h" +#include "iCalPerson.h" +#include "common.h" + +@interface NSDate(UsedPrivates) +- (NSString *)icalString; // declared in NGiCal +@end + +@implementation iCalRenderer + +static iCalRenderer *renderer = nil; + +/* assume length of 1K - reasonable ? */ +static unsigned DefaultICalStringCapacity = 1024; + ++ (id)sharedICalendarRenderer { + if (renderer == nil) + renderer = [[self alloc] init]; + return renderer; +} + +/* renderer */ + +- (void)addPreambleForAppointment:(iCalEvent *)_apt + toString:(NSMutableString *)s +{ + [s appendString:@"BEGIN:VCALENDAR\r\nMETHOD:REQUEST\r\n"]; + [s appendString:@"PRODID:NGiCal/4.3\r\n"]; + [s appendString:@"VERSION:2.0\r\n"]; +} +- (void)addPostambleForAppointment:(iCalEvent *)_apt + toString:(NSMutableString *)s +{ + [s appendString:@"END:VCALENDAR\r\n"]; +} + +- (void)addOrganizer:(iCalPerson *)p toString:(NSMutableString *)s { + NSString *x; + + if (![p isNotNull]) return; + + [s appendString:@"ORGANIZER;CN=\""]; + if ((x = [p cn])) + [s appendString:[x iCalDQUOTESafeString]]; + + [s appendString:@"\""]; + if ((x = [p email])) { + [s appendString:@":"]; /* sic! */ + [s appendString:[x iCalSafeString]]; + } + [s appendString:@"\r\n"]; +} + +- (void)addAttendees:(NSArray *)persons toString:(NSMutableString *)s { + unsigned i, count; + iCalPerson *p; + + count = [persons count]; + for (i = 0; i < count; i++) { + NSString *x; + + p = [persons objectAtIndex:i]; + [s appendString:@"ATTENDEE;"]; + + if ((x = [p role])) { + [s appendString:@"ROLE="]; + [s appendString:[x iCalSafeString]]; + [s appendString:@";"]; + } + + [s appendString:@"CN=\""]; + if ((x = [p cn])) { + [s appendString:[x iCalDQUOTESafeString]]; + } + [s appendString:@"\""]; + if ([(x = [p email]) isNotNull]) { + [s appendString:@":"]; /* sic! */ + [s appendString:[x iCalSafeString]]; + } + [s appendString:@"\r\n"]; + } +} + +- (void)addVEventForAppointment:(iCalEvent *)event + toString:(NSMutableString *)s +{ + [s appendString:@"BEGIN:VEVENT\r\n"]; + + [s appendString:@"SUMMARY:"]; + [s appendString:[[event summary] iCalSafeString]]; + [s appendString:@"\r\n"]; + if ([[event location] length] > 0) { + [s appendString:@"LOCATION:"]; + [s appendString:[[event location] iCalSafeString]]; + [s appendString:@"\r\n"]; + } + [s appendString:@"UID:"]; + [s appendString:[event uid]]; + [s appendString:@"\r\n"]; + + [s appendString:@"DTSTART:"]; + [s appendString:[[event startDate] icalString]]; + [s appendString:@"\r\n"]; + + if ([event hasEndDate]) { + [s appendString:@"DTEND:"]; + [s appendString:[[event endDate] icalString]]; + [s appendString:@"\r\n"]; + } + if ([event hasDuration]) { + [s appendString:@"DURATION:"]; + [s appendString:[event duration]]; + [s appendString:@"\r\n"]; + } + if ([[event priority] length] > 0) { + [s appendString:@"PRIORITY:"]; + [s appendString:[event priority]]; + [s appendString:@"\r\n"]; + } + if ([[event categories] length] > 0) { + NSString *catString; + + catString = [event categories]; + [s appendString:@"CATEGORIES:"]; + [s appendString:catString]; + [s appendString:@"\r\n"]; + } + if ([[event comment] length] > 0) { + [s appendString:@"DESCRIPTION:"]; /* this is what iCal.app does */ + [s appendString:[[event comment] iCalSafeString]]; + [s appendString:@"\r\n"]; + } + + [s appendString:@"STATUS:"]; + [s appendString:[event status]]; + [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'] */ + + [self addOrganizer:[event organizer] toString:s]; + [self addAttendees:[event attendees] toString:s]; + + /* postamble */ + [s appendString:@"END:VEVENT\r\n"]; +} + +- (BOOL)isValidAppointment:(iCalEvent *)_apt { + if (![_apt isNotNull]) + return NO; + + if ([[_apt uid] length] == 0) { + [self logWithFormat: + @"WARNING: 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]; + return NO; + } + + return YES; +} + +- (NSString *)vEventStringForEvent:(iCalEvent *)_apt { + NSMutableString *s; + + if (![self isValidAppointment:_apt]) + return nil; + + s = [NSMutableString stringWithCapacity:DefaultICalStringCapacity]; + [self addVEventForAppointment:_apt toString:s]; + return s; +} + +- (NSString *)iCalendarStringForEvent:(iCalEvent *)_apt { + NSMutableString *s; + + if (![self isValidAppointment:_apt]) + return nil; + + s = [NSMutableString stringWithCapacity:DefaultICalStringCapacity]; + [self addPreambleForAppointment:_apt toString:s]; + [self addVEventForAppointment:_apt toString:s]; + [self addPostambleForAppointment:_apt toString:s]; + return s; +} + +@end /* iCalRenderer */ + +@interface NSString (SOGoiCal_Private) +- (NSString *)iCalCleanString; +@end + +@interface SOGoICalStringEscaper : NSObject +{ +} ++ (id)sharedEscaper; +@end + +@implementation SOGoICalStringEscaper ++ (id)sharedEscaper { + static id sharedInstance = nil; + if (!sharedInstance) { + sharedInstance = [[self alloc] init]; + } + return sharedInstance; +} + +- (NSString *)stringByEscapingString:(NSString *)_s { + unichar c; + + if (!_s || [_s length] == 0) + return nil; + + c = [_s characterAtIndex:0]; + if (c == '\n') { + return @"\\n"; + } + else if (c == '\r') { + return nil; /* effectively remove char */ + } + return [NSString stringWithFormat:@"\\%@", _s]; +} + +@end + +@implementation NSString (SOGoiCal) + +#if 0 +- (NSString *)iCalFoldedString { + /* RFC2445, 4.1 Content Lines + + The iCalendar object is organized into individual lines of text, + called content lines. Content lines are delimited by a line break, + which is a CRLF sequence (US-ASCII decimal 13, followed by US-ASCII + decimal 10). + Lines of text SHOULD NOT be longer than 75 octets, excluding the line + break. Long content lines SHOULD be split into a multiple line + representations using a line "folding" technique. That is, a long + line can be split between any two characters by inserting a CRLF + immediately followed by a single linear white space character (i.e., + SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). + Any sequence of CRLF followed immediately by a single linear white space + character is ignored (i.e., removed) when processing the content type. + */ +} +#endif + +/* strip off any characters from string which are not allowed in iCal */ +- (NSString *)iCalCleanString { + static NSCharacterSet *replaceSet = nil; + + if (replaceSet == nil) { + replaceSet = [NSCharacterSet characterSetWithCharactersInString:@"\r"]; + [replaceSet retain]; + } + + return [self stringByEscapingCharactersFromSet:replaceSet + usingStringEscaping:[SOGoICalStringEscaper sharedEscaper]]; +} + +- (NSString *)iCalDQUOTESafeString { + static NSCharacterSet *escapeSet = nil; + + if (escapeSet == nil) { + escapeSet = [NSCharacterSet characterSetWithCharactersInString:@"\\\""]; + [escapeSet retain]; + } + return [self iCalEscapedStringWithEscapeSet:escapeSet]; +} + +- (NSString *)iCalSafeString { + static NSCharacterSet *escapeSet = nil; + + if (escapeSet == nil) { + escapeSet = + [[NSCharacterSet characterSetWithCharactersInString:@"\n,;\\\""] retain]; + } + return [self iCalEscapedStringWithEscapeSet:escapeSet]; +} + +/* Escape unsafe characters */ +- (NSString *)iCalEscapedStringWithEscapeSet:(NSCharacterSet *)_es { + NSString *s; + + s = [self iCalCleanString]; + return [s stringByEscapingCharactersFromSet:_es + usingStringEscaping:[SOGoICalStringEscaper sharedEscaper]]; +} + +@end /* NSString (SOGoiCal) */