From da9d350573bd9b99957de1ab372ef55872a00dfc Mon Sep 17 00:00:00 2001 From: znek Date: Fri, 17 Dec 2004 18:17:07 +0000 Subject: [PATCH] fix for SOGo Bug #1051 git-svn-id: http://svn.opengroupware.org/SOGo/trunk@479 d1b88da0-ebda-0310-925b-ed51d893ca5b --- OGoContentStore/ChangeLog | 9 + OGoContentStore/OCSiCalFieldExtractor.m | 61 ++- .../OGoContentStore.xcode/project.pbxproj | 31 ++ OGoContentStore/Version | 3 +- OGoContentStore/sql/appointment-create.psql | 5 + .../sql/foldertablecreate-helge-privcal.psql | 1 + .../sql/generate-folderinfo-sql-for-users.sh | 1 + SOGo/SOGo.xcode/project.pbxproj | 38 +- .../Appointments/SOGoAppointmentFolder.h | 7 + .../Appointments/SOGoAppointmentFolder.m | 53 +- SOGo/SoObjects/Appointments/Version | 2 +- SOGo/SoObjects/ChangeLog | 6 + SOGo/UI/Anais/AnaisAttendeeSelector.m | 44 +- SOGo/UI/Anais/AnaisAttendeeSelector.wox | 4 +- SOGo/UI/Anais/ChangeLog | 4 + SOGo/UI/Anais/Version | 2 +- SOGo/UI/SOGoUI/ChangeLog | 5 + SOGo/UI/SOGoUI/SOGoAptFormatter.m | 6 +- SOGo/UI/SOGoUI/Version | 2 +- SOGo/UI/Scheduler/ChangeLog | 27 ++ .../Scheduler/English.lproj/default.strings | 13 +- SOGo/UI/Scheduler/GNUmakefile | 59 +-- SOGo/UI/Scheduler/UIxAppointmentEditor.m | 130 ++++- SOGo/UI/Scheduler/UIxAppointmentFormatter.h | 121 ----- SOGo/UI/Scheduler/UIxAppointmentFormatter.m | 452 ------------------ SOGo/UI/Scheduler/UIxAppointmentView.m | 16 - SOGo/UI/Scheduler/UIxAppointmentView.wox | 24 +- .../Scheduler/UIxCalParticipationStatusView.m | 66 +++ .../UIxCalParticipationStatusView.wox | 8 + SOGo/UI/Scheduler/UIxCalScheduleOverview.m | 85 +++- SOGo/UI/Scheduler/UIxCalScheduleOverview.wox | 148 ++++-- SOGo/UI/Scheduler/UIxCalView.m | 9 +- SOGo/UI/Scheduler/Version | 2 +- SOGo/UI/Scheduler/product.plist | 18 +- SOGoLogic/ChangeLog | 9 + SOGoLogic/SOGoAppointment.h | 6 +- SOGoLogic/SOGoAppointment.m | 17 + SOGoLogic/SOGoAppointmentICalRenderer.m | 10 +- SOGoLogic/Version | 3 +- 39 files changed, 709 insertions(+), 798 deletions(-) delete mode 100644 SOGo/UI/Scheduler/UIxAppointmentFormatter.h delete mode 100644 SOGo/UI/Scheduler/UIxAppointmentFormatter.m create mode 100644 SOGo/UI/Scheduler/UIxCalParticipationStatusView.m create mode 100644 SOGo/UI/Scheduler/UIxCalParticipationStatusView.wox diff --git a/OGoContentStore/ChangeLog b/OGoContentStore/ChangeLog index fe5320fa..e70e89c5 100644 --- a/OGoContentStore/ChangeLog +++ b/OGoContentStore/ChangeLog @@ -1,3 +1,12 @@ +2004-12-17 Marcus Mueller + + * v0.9.18 + + * OCSiCalFieldExtractor.m: extract participants' state + + * sql/generate-folderinfo-sql-for-user.sh, sql/appointment-create.psql, + sql/foldertablecreate-helge-privcal.psql: updated with new schema. + 2004-12-15 Marcus Mueller * OCSiCalFieldExtractor.m: partmails + cn's are concatenated by '\n' diff --git a/OGoContentStore/OCSiCalFieldExtractor.m b/OGoContentStore/OCSiCalFieldExtractor.m index c1b9492f..6afdac26 100644 --- a/OGoContentStore/OCSiCalFieldExtractor.m +++ b/OGoContentStore/OCSiCalFieldExtractor.m @@ -65,12 +65,15 @@ static OCSiCalFieldExtractor *extractor = nil; - (NSMutableDictionary *)extractQuickFieldsFromEvent:(iCalEvent *)_event { NSMutableDictionary *row; - NSCalendarDate *startDate, *endDate; - NSString *uid, *title, *location, *status, *accessClass; - NSNumber *sequence; - id organizer; - id participants, partmails; - + NSCalendarDate *startDate, *endDate; + NSArray *attendees; + NSString *uid, *title, *location, *status, *accessClass; + NSNumber *sequence; + id organizer; + id participants, partmails; + NSMutableString *partstates; + unsigned i, count; + if (_event == nil) return nil; @@ -85,10 +88,10 @@ static OCSiCalFieldExtractor *extractor = nil; accessClass = [[_event accessClass] uppercaseString]; status = [[_event status] uppercaseString]; - participants = [_event attendees]; - partmails = [participants valueForKey:@"rfc822Email"]; + attendees = [_event attendees]; + partmails = [attendees valueForKey:@"rfc822Email"]; partmails = [partmails componentsJoinedByString:@"\n"]; - participants = [participants valueForKey:@"cn"]; + participants = [attendees valueForKey:@"cn"]; participants = [participants componentsJoinedByString:@"\n"]; /* build row */ @@ -123,16 +126,16 @@ static OCSiCalFieldExtractor *extractor = nil; if ([status isNotNull]) { int code = 1; - - if ([status isEqualToString:@"TENTATIVE"]) - code = 0; - else if ([status isEqualToString:@"CANCELLED"]) - code = 2; + + if ([status isEqualToString:@"TENTATIVE"]) + code = 0; + else if ([status isEqualToString:@"CANCELLED"]) + code = 2; [row setObject:[NSNumber numberWithInt:code] forKey:@"status"]; } else { - /* confirmed by default */ - [row setObject:[NSNumber numberWithInt:1] forKey:@"status"]; + /* confirmed by default */ + [row setObject:[NSNumber numberWithInt:1] forKey:@"status"]; } if([accessClass isNotNull] && ![accessClass isEqualToString:@"PUBLIC"]) { @@ -144,12 +147,28 @@ static OCSiCalFieldExtractor *extractor = nil; organizer = [_event organizer]; if (organizer) { - NSString *email; - - email = [organizer valueForKey:@"rfc822Email"]; - if (email) - [row setObject:email forKey:@"orgmail"]; + NSString *email; + + email = [organizer valueForKey:@"rfc822Email"]; + if (email) + [row setObject:email forKey:@"orgmail"]; + } + + /* construct partstates */ + count = [attendees count]; + partstates = [[NSMutableString alloc] initWithCapacity:count * 2]; + for ( i = 0; i < count; i++) { + iCalPerson *p; + iCalPersonPartStat stat; + + p = [attendees objectAtIndex:i]; + stat = [p participationStatus]; + if(i != 0) + [partstates appendString:@"\n"]; + [partstates appendFormat:@"%d", stat]; } + [row setObject:partstates forKey:@"partstates"]; + [partstates release]; return row; } diff --git a/OGoContentStore/OGoContentStore.xcode/project.pbxproj b/OGoContentStore/OGoContentStore.xcode/project.pbxproj index 6bf03552..1266e6a7 100644 --- a/OGoContentStore/OGoContentStore.xcode/project.pbxproj +++ b/OGoContentStore/OGoContentStore.xcode/project.pbxproj @@ -73,6 +73,7 @@ }; AD0CF94D071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; @@ -105,6 +106,7 @@ }; AD0CF951071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EOAdaptorChannel+OCS.h"; @@ -113,6 +115,7 @@ }; AD0CF952071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "EOAdaptorChannel+OCS.m"; @@ -121,6 +124,7 @@ }; AD0CF953071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EOQualifier+OCS.h"; @@ -129,6 +133,7 @@ }; AD0CF954071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "EOQualifier+OCS.m"; @@ -229,6 +234,7 @@ }; AD0CF95F071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURL+OCS.h"; @@ -237,6 +243,7 @@ }; AD0CF960071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURL+OCS.m"; @@ -245,6 +252,7 @@ }; AD0CF961071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ocs_cat.m; @@ -253,6 +261,7 @@ }; AD0CF962071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ocs_ls.m; @@ -261,6 +270,7 @@ }; AD0CF963071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ocs_mkdir.m; @@ -269,6 +279,7 @@ }; AD0CF964071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ocs_recreatequick.m; @@ -277,6 +288,7 @@ }; AD0CF965071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSChannelManager.h; @@ -285,6 +297,7 @@ }; AD0CF966071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSChannelManager.m; @@ -293,6 +306,7 @@ }; AD0CF967071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSContactFieldExtractor.m; @@ -301,6 +315,7 @@ }; AD0CF968071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSContext.h; @@ -309,6 +324,7 @@ }; AD0CF969071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSContext.m; @@ -317,6 +333,7 @@ }; AD0CF96A071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSFieldExtractor.h; @@ -325,6 +342,7 @@ }; AD0CF96B071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSFieldExtractor.m; @@ -333,6 +351,7 @@ }; AD0CF96C071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSFieldInfo.h; @@ -341,6 +360,7 @@ }; AD0CF96D071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSFieldInfo.m; @@ -349,6 +369,7 @@ }; AD0CF96E071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSFolder.h; @@ -357,6 +378,7 @@ }; AD0CF96F071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSFolder.m; @@ -365,6 +387,7 @@ }; AD0CF970071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSFolderManager.h; @@ -373,6 +396,7 @@ }; AD0CF971071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSFolderManager.m; @@ -381,6 +405,7 @@ }; AD0CF972071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSFolderType.h; @@ -389,6 +414,7 @@ }; AD0CF973071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSFolderType.m; @@ -397,6 +423,7 @@ }; AD0CF974071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSiCalFieldExtractor.h; @@ -405,6 +432,7 @@ }; AD0CF975071FE18800E72147 = { fileEncoding = 5; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCSiCalFieldExtractor.m; @@ -629,6 +657,7 @@ AD0CF952071FE18800E72147, AD0CF954071FE18800E72147, ); + indentWidth = 2; isa = PBXGroup; name = Classes; refType = 4; @@ -649,6 +678,7 @@ AD0CF951071FE18800E72147, AD0CF953071FE18800E72147, ); + indentWidth = 2; isa = PBXGroup; name = Headers; path = ""; @@ -657,6 +687,7 @@ }; AD0CF9CC071FE4A400E72147 = { fileEncoding = 4; + indentWidth = 2; isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCSStringFormatter.h; diff --git a/OGoContentStore/Version b/OGoContentStore/Version index f4834424..1adba52a 100644 --- a/OGoContentStore/Version +++ b/OGoContentStore/Version @@ -2,8 +2,9 @@ MAJOR_VERSION=0 MINOR_VERSION=9 -SUBMINOR_VERSION:=17 +SUBMINOR_VERSION:=18 +# v0.9.18 requires libNGiCal v4.5.38 # v0.9.17 requires libNGiCal v4.5.37 # v0.9.11 requires libFoundation v1.0.63 # v0.9.11 requires libNGExtensions v4.3.125 diff --git a/OGoContentStore/sql/appointment-create.psql b/OGoContentStore/sql/appointment-create.psql index a9283998..7b280be7 100644 --- a/OGoContentStore/sql/appointment-create.psql +++ b/OGoContentStore/sql/appointment-create.psql @@ -12,8 +12,13 @@ CREATE TABLE %s_quick ( participants VARCHAR(100000) NOT NULL, -- the CNs of the participants isallday INT NULL, iscycle INT NULL, -- client needs to fetch to resolve + ispublic INT NOT NULL, + status INT NOT NULL, + isopaque INT NULL, location VARCHAR(256) NULL, + orgmail VARCHAR(256) NULL, partmails VARCHAR(100000) NOT NULL, -- the emails of the participants + partstates VARCHAR(256) NOT NULL, -- the status of each participant sequence INT NULL -- the iCal sequence ); diff --git a/OGoContentStore/sql/foldertablecreate-helge-privcal.psql b/OGoContentStore/sql/foldertablecreate-helge-privcal.psql index 11334170..558a714a 100644 --- a/OGoContentStore/sql/foldertablecreate-helge-privcal.psql +++ b/OGoContentStore/sql/foldertablecreate-helge-privcal.psql @@ -21,6 +21,7 @@ CREATE TABLE SOGo_helge_privcal_quick ( location VARCHAR(256) NULL, orgmail VARCHAR(256) NULL, partmails VARCHAR(100000) NOT NULL, -- the emails of the participants + partstates VARCHAR(256) NOT NULL, -- the status of each participant sequence INT NULL -- the iCal sequence ); diff --git a/OGoContentStore/sql/generate-folderinfo-sql-for-users.sh b/OGoContentStore/sql/generate-folderinfo-sql-for-users.sh index 5ad0df65..2b054a3a 100755 --- a/OGoContentStore/sql/generate-folderinfo-sql-for-users.sh +++ b/OGoContentStore/sql/generate-folderinfo-sql-for-users.sh @@ -82,6 +82,7 @@ CREATE TABLE SOGo_${USER_TABLE}_privcal_quick ( location VARCHAR(256) NULL, orgmail VARCHAR(256) NULL, partmails VARCHAR(100000) NOT NULL, -- the emails of the participants + partstates VARCHAR(256) NOT NULL, -- the status of each participant sequence INT NULL -- the iCal sequence ); diff --git a/SOGo/SOGo.xcode/project.pbxproj b/SOGo/SOGo.xcode/project.pbxproj index 3ea15f34..8a4c697f 100644 --- a/SOGo/SOGo.xcode/project.pbxproj +++ b/SOGo/SOGo.xcode/project.pbxproj @@ -181,6 +181,8 @@ E87208F40692E3D30099CBBD, E87208FA0692E3D30099CBBD, E87208FB0692E3D30099CBBD, + AD273D4D077315640064794B, + AD273D4C077315640064794B, ); isa = PBXGroup; name = Appointment; @@ -643,6 +645,22 @@ sourceTree = ""; tabWidth = 2; }; + AD273D4C077315640064794B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = text.xml; + path = UIxCalParticipationStatusView.wox; + refType = 4; + sourceTree = ""; + }; + AD273D4D077315640064794B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.objc; + path = UIxCalParticipationStatusView.m; + refType = 4; + sourceTree = ""; + }; AD2C74A1071A9FF70087E027 = { fileEncoding = 5; isa = PBXFileReference; @@ -4039,24 +4057,6 @@ refType = 4; sourceTree = ""; }; - E87208F50692E3D30099CBBD = { - fileEncoding = 5; - indentWidth = 2; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - path = UIxAppointmentFormatter.h; - refType = 4; - sourceTree = ""; - }; - E87208F60692E3D30099CBBD = { - fileEncoding = 5; - indentWidth = 2; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.objc; - path = UIxAppointmentFormatter.m; - refType = 4; - sourceTree = ""; - }; E87208F70692E3D30099CBBD = { fileEncoding = 5; indentWidth = 2; @@ -4285,8 +4285,6 @@ children = ( E87208900692E3D30099CBBD, E87208E40692E3D30099CBBD, - E87208F50692E3D30099CBBD, - E87208F60692E3D30099CBBD, E87209060692E3D30099CBBD, E87209070692E3D30099CBBD, AD5ED1AE06B1768900E3EC4B, diff --git a/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.h b/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.h index 882018c0..2e62d932 100644 --- a/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -61,6 +61,13 @@ - (NSArray *)fetchCoreInfosFrom:(NSCalendarDate *)_startDate to:(NSCalendarDate *)_endDate; +- (NSArray *)fetchOverviewInfosFromFolder:(OCSFolder *)_folder + from:(NSCalendarDate *)_startDate + to:(NSCalendarDate *)_endDate; + +- (NSArray *)fetchOverviewInfosFrom:(NSCalendarDate *)_startDate + to:(NSCalendarDate *)_endDate; + /* URL generation */ - (NSString *)baseURLForAptWithUID:(NSString *)_uid inContext:(id)_ctx; diff --git a/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m b/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m index a5371bff..9412ffb5 100644 --- a/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SOGo/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -208,21 +208,52 @@ static NSTimeZone *MET = nil; return records; } +- (NSArray *)fetchOverviewInfosFromFolder:(OCSFolder *)_folder + from:(NSCalendarDate *)_startDate + to:(NSCalendarDate *)_endDate +{ + static NSArray *infos = nil; + if(!infos) { + infos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate", + @"title", @"location", @"orgmail", + @"status", @"ispublic", @"iscycle", + @"isallday", + nil] retain]; + } + return [self fetchFields:infos + fromFolder:_folder + from:_startDate + to:_endDate]; +} + +- (NSArray *)fetchOverviewInfosFrom:(NSCalendarDate *)_startDate + to:(NSCalendarDate *)_endDate +{ + OCSFolder *folder; + + if ((folder = [self ocsFolder]) == nil) { + [self errorWithFormat:@"(%s): missing folder for fetch!", + __PRETTY_FUNCTION__]; + return nil; + } + return [self fetchOverviewInfosFromFolder:folder from:_startDate to:_endDate]; +} + - (NSArray *)fetchCoreInfosFromFolder:(OCSFolder *)_folder from:(NSCalendarDate *)_startDate to:(NSCalendarDate *)_endDate { - static NSArray *coreInfos = nil; - if(!coreInfos) { - coreInfos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate", - @"title", @"participants", - @"location", @"isallday", - @"iscycle", @"partmails", - @"sequence", @"ispublic", - @"isopaque", @"status", @"orgmail", - nil] retain]; + static NSArray *infos = nil; + if(!infos) { + infos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate", + @"title", @"location", @"orgmail", + @"status", @"ispublic", @"iscycle", + @"isallday", @"isopaque", + @"participants", @"partmails", + @"partstates", @"sequence", + nil] retain]; } - return [self fetchFields:coreInfos + return [self fetchFields:infos fromFolder:_folder from:_startDate to:_endDate]; @@ -236,7 +267,7 @@ static NSTimeZone *MET = nil; if ((folder = [self ocsFolder]) == nil) { [self errorWithFormat:@"(%s): missing folder for fetch!", - __PRETTY_FUNCTION__]; + __PRETTY_FUNCTION__]; return nil; } return [self fetchCoreInfosFromFolder:folder from:_startDate to:_endDate]; diff --git a/SOGo/SoObjects/Appointments/Version b/SOGo/SoObjects/Appointments/Version index 63f19036..ed0844b5 100644 --- a/SOGo/SoObjects/Appointments/Version +++ b/SOGo/SoObjects/Appointments/Version @@ -1,6 +1,6 @@ # Version file -SUBMINOR_VERSION:=22 +SUBMINOR_VERSION:=23 # v0.9.19 requires NGiCal v4.5.36 # v0.9.13 requires libSOGo v0.9.26 diff --git a/SOGo/SoObjects/ChangeLog b/SOGo/SoObjects/ChangeLog index 9818b304..104f80ed 100644 --- a/SOGo/SoObjects/ChangeLog +++ b/SOGo/SoObjects/ChangeLog @@ -1,3 +1,9 @@ +2004-12-17 Marcus Mueller + + * Appointments/SOGoAppointmentFolder.[hm]: added "partstates" to + coreInfos for fetching. Also added new API to fetch a stripped down + version of coreInfos, suitable for overviews. (v0.9.23) + 2004-12-15 Marcus Mueller * Appointments/SOGoAppointmentFolder.m: default redirect is now diff --git a/SOGo/UI/Anais/AnaisAttendeeSelector.m b/SOGo/UI/Anais/AnaisAttendeeSelector.m index d8474b2c..728c2f13 100644 --- a/SOGo/UI/Anais/AnaisAttendeeSelector.m +++ b/SOGo/UI/Anais/AnaisAttendeeSelector.m @@ -168,22 +168,25 @@ static BOOL debugOn = NO; return [self hasNoAttendees] && [[self emailForUser] length] > 0 ? YES : NO; } -/* email, cn */ +/* email, stat, cn */ -- (NSString *)combinedCNAndEmailForUser { +- (NSString *)combinedInfoForUser { NSString *e, *c; e = [self emailForUser]; c = [self cnForUser]; - return [[e stringByAppendingString:@";"] stringByAppendingString:c]; + return [[e stringByAppendingString:@";0;"] stringByAppendingString:c]; } -- (NSString *)combinedCNAndEmail { - NSString *e, *c; +- (NSString *)combinedInfo { + iCalPerson *p; + NSString *e, *stat, *c; - e = [[self attendee] rfc822Email]; - c = [[self attendee] cnForDisplay]; - return [[e stringByAppendingString:@";"] stringByAppendingString:c]; + p = [self attendee]; + e = [p rfc822Email]; + c = [p cnForDisplay]; + stat = [NSString stringWithFormat:@";%d;", [p participationStatus]]; + return [[e stringByAppendingString:stat] stringByAppendingString:c]; } /* id accessors */ @@ -231,14 +234,26 @@ static BOOL debugOn = NO; pString = [_values objectAtIndex:i]; if ([pString length] == 0) continue; - - /* delimiter between email and cn */ + + p = [[iCalPerson alloc] init]; + + /* sample: 'test.et.di.cete-lyon@equipement.gouv.fr;1;test' */ + /* delimiter between email and partStat */ r = [pString rangeOfString:@";"]; if (r.length > 0) { - /* sample: 'test.et.di.cete-lyon@equipement.gouv.fr;test' */ email = [pString substringToIndex:r.location]; - cn = [pString substringFromIndex:NSMaxRange(r)]; - if ([cn length] == 0) cn = nil; + + pString = [pString substringFromIndex:NSMaxRange(r)]; + /* delimiter between partStat and CN */ + r = [pString rangeOfString:@";"]; + if (r.length > 0) { + NSString *stat; + + stat = [pString substringToIndex:r.location]; + [p setParticipationStatus:[stat intValue]]; + cn = [pString substringFromIndex:NSMaxRange(r)]; + if ([cn length] == 0) cn = nil; + } } else { email = pString; @@ -250,7 +265,6 @@ static BOOL debugOn = NO; cn = [um getCNForUID:[um getUIDForEmail:email]]; } - p = [[iCalPerson alloc] init]; [p setEmail:[@"mailto:" stringByAppendingString:email]]; if ([cn isNotNull]) [p setCn:cn]; @@ -305,7 +319,7 @@ static BOOL debugOn = NO; @" checkbox = document.createElement('input');\n" @" checkbox.setAttribute('type', 'checkbox');\n" @" checkbox.setAttribute('checked', 'checked');\n" - @" checkbox.setAttribute('value', email + ';' + cn);\n" + @" checkbox.setAttribute('value', email + ';0;' + cn);\n" @" checkbox.setAttribute('id', email);\n" @" checkbox.setAttribute('name', tableId);\n" @" td.appendChild(checkbox);\n" diff --git a/SOGo/UI/Anais/AnaisAttendeeSelector.wox b/SOGo/UI/Anais/AnaisAttendeeSelector.wox index 893390ee..410f74d9 100644 --- a/SOGo/UI/Anais/AnaisAttendeeSelector.wox +++ b/SOGo/UI/Anais/AnaisAttendeeSelector.wox @@ -27,7 +27,7 @@ @@ -40,7 +40,7 @@ diff --git a/SOGo/UI/Anais/ChangeLog b/SOGo/UI/Anais/ChangeLog index 78c05da3..a6037246 100644 --- a/SOGo/UI/Anais/ChangeLog +++ b/SOGo/UI/Anais/ChangeLog @@ -1,3 +1,7 @@ +2004-12-17 Marcus Mueller + + * AnaisAttendeeSelector.m: preserve participation status (v0.9.18) + 2004-10-20 Marcus Mueller * AnaisAttendeeSelector.m: provide proper unescaping of special HTML diff --git a/SOGo/UI/Anais/Version b/SOGo/UI/Anais/Version index f929c4ce..e138e47c 100644 --- a/SOGo/UI/Anais/Version +++ b/SOGo/UI/Anais/Version @@ -1,3 +1,3 @@ # $Id: Version 165 2004-08-05 17:55:50Z znek $ -SUBMINOR_VERSION:=17 +SUBMINOR_VERSION:=18 diff --git a/SOGo/UI/SOGoUI/ChangeLog b/SOGo/UI/SOGoUI/ChangeLog index f220d322..3a950e3d 100644 --- a/SOGo/UI/SOGoUI/ChangeLog +++ b/SOGo/UI/SOGoUI/ChangeLog @@ -1,3 +1,8 @@ +2004-12-17 Marcus Mueller + + * SOGoAptFormatter.m: remove "appointment" prefix from tooltips + (v0.9.22) + 2004-12-13 Marcus Mueller * SOGoAptFormatter.[hm]: new option for formatter (v0.9.21) diff --git a/SOGo/UI/SOGoUI/SOGoAptFormatter.m b/SOGo/UI/SOGoUI/SOGoAptFormatter.m index 0694429a..e468d783 100644 --- a/SOGo/UI/SOGoUI/SOGoAptFormatter.m +++ b/SOGo/UI/SOGoUI/SOGoAptFormatter.m @@ -185,8 +185,7 @@ spansRange = ![endDate isEqualToDate:startDate]; } aptDescr = [NSMutableString stringWithCapacity:60]; - [aptDescr appendString:@"appointment"]; - [aptDescr appendFormat:@"\n%02i:%02i", + [aptDescr appendFormat:@"%02i:%02i", [startDate hourOfDay], [startDate minuteOfHour]]; if (spansRange) { @@ -218,8 +217,7 @@ spansRange = ![endDate isEqualToDate:startDate]; } aptDescr = [NSMutableString stringWithCapacity:25]; - [aptDescr appendString:@"appointment"]; - [aptDescr appendFormat:@"\n%02i:%02i", + [aptDescr appendFormat:@"%02i:%02i", [startDate hourOfDay], [startDate minuteOfHour]]; if (spansRange) { diff --git a/SOGo/UI/SOGoUI/Version b/SOGo/UI/SOGoUI/Version index c0af8cd7..dd5eff26 100644 --- a/SOGo/UI/SOGoUI/Version +++ b/SOGo/UI/SOGoUI/Version @@ -1,5 +1,5 @@ # $Id$ -SUBMINOR_VERSION:=21 +SUBMINOR_VERSION:=22 # v0.9.18 requires NGExtensions v4.5.136 diff --git a/SOGo/UI/Scheduler/ChangeLog b/SOGo/UI/Scheduler/ChangeLog index 1284cce4..5e15dc34 100644 --- a/SOGo/UI/Scheduler/ChangeLog +++ b/SOGo/UI/Scheduler/ChangeLog @@ -1,3 +1,30 @@ +2004-12-17 Marcus Mueller + + * v0.9.104 + + * GNUmakefile: removed UIxAppointmentFormatter + + * UIxAppointmentFormatter.[hm]: removed, never in use + + * UIxCalParticipationStatusView.[wox,m]: new view to display localized + participation status + + * UIxCalView.m: fetch OverviewInfos instead of CoreInfos (should be + faster) + + * UIxCalScheduleOverview.[wox,m]: completed the schedule overview, + display participation state for all participants. + + * product.plist: renamed "reject" to "decline" and moved from + UIxAppointmentView to UIxAppointmentEditor. + + * UIxAppointmentView.m: removed "accept" and "reject" action stubs + + * UIxAppointmentEditor.[wox,m]: implemented "accept" and "decline". + Changed iCal template a bit, particularly removed getOrganizer - + with every save the organizer was changed also which was horribly + wrong. + 2004-12-16 Marcus Mueller * v0.9.103 diff --git a/SOGo/UI/Scheduler/English.lproj/default.strings b/SOGo/UI/Scheduler/English.lproj/default.strings index fca12bbe..f1c14fdc 100644 --- a/SOGo/UI/Scheduler/English.lproj/default.strings +++ b/SOGo/UI/Scheduler/English.lproj/default.strings @@ -95,7 +95,8 @@ "sched_startDateFormat" = "%d.%m. %H:%M"; "action" = "Actions"; "accept" = "Accept"; -"reject" = "Reject"; +"decline" = "Decline"; +"more participants" = "more participants"; /* Appointments */ @@ -115,6 +116,7 @@ "Title" = "Title"; "Name" = "Name"; "Email" = "Email"; +"Status" = "Status"; "Location" = "Location"; "Priority" = "Priority"; "Categories" = "Categories"; @@ -135,6 +137,15 @@ /* text used in overviews and tooltips */ "private appointment" = "Private appointment"; +/* Appointments (participation state) */ + +"partStat_NEEDS-ACTION" = "Not decided, yet"; +"partStat_ACCEPTED" = "Accepted"; +"partStat_DECLINED" = "Declined"; +"partStat_TENTATIVE" = "Tentative"; +"partStat_DELEGATED" = "Delegated"; +"partStat_OTHER" = "???"; + /* Searching */ diff --git a/SOGo/UI/Scheduler/GNUmakefile b/SOGo/UI/Scheduler/GNUmakefile index ffd914f7..f5db8afe 100644 --- a/SOGo/UI/Scheduler/GNUmakefile +++ b/SOGo/UI/Scheduler/GNUmakefile @@ -10,7 +10,6 @@ SchedulerUI_LANGUAGES = English French SchedulerUI_OBJC_FILES = \ SchedulerUIProduct.m \ - UIxAppointmentFormatter.m \ iCalPerson+UIx.m \ NSCalendarDate+UIx.m \ SOGoAppointment+UIx.m \ @@ -36,7 +35,7 @@ SchedulerUI_OBJC_FILES = \ UIxCalYearOverview.m \ UIxCalInlineMonthOverview.m \ UIxAppointmentView.m \ - UIxAppointmentPrintview.m \ + UIxAppointmentPrintview.m \ UIxAppointmentEditor.m \ UIxCalSelectTab.m \ UIxCalDateLabel.m \ @@ -48,39 +47,41 @@ SchedulerUI_OBJC_FILES = \ UIxTimeDateControl.m \ UIxCalInlineAptView.m \ UIxCalScheduleOverview.m \ + UIxCalParticipationStatusView.m \ SchedulerUI_RESOURCE_FILES += \ Version \ product.plist -SchedulerUI_RESOURCE_FILES += \ - UIxAptTableView.wox \ - UIxCalDayOverview.wox \ - UIxCalDayChartview.wox \ - UIxCalDayListview.wox \ - UIxCalDayPrintview.wox \ - UIxCalWeekOverview.wox \ - UIxCalWeekChartview.wox \ - UIxCalWeekListview.wox \ - UIxCalWeekColumnsview.wox \ - UIxCalWeekPrintview.wox \ - UIxCalMonthOverview.wox \ - UIxCalMonthPrintview.wox \ - UIxCalYearOverview.wox \ - UIxCalInlineMonthOverview.wox \ - UIxAppointmentView.wox \ +SchedulerUI_RESOURCE_FILES += \ + UIxAptTableView.wox \ + UIxCalDayOverview.wox \ + UIxCalDayChartview.wox \ + UIxCalDayListview.wox \ + UIxCalDayPrintview.wox \ + UIxCalWeekOverview.wox \ + UIxCalWeekChartview.wox \ + UIxCalWeekListview.wox \ + UIxCalWeekColumnsview.wox \ + UIxCalWeekPrintview.wox \ + UIxCalMonthOverview.wox \ + UIxCalMonthPrintview.wox \ + UIxCalYearOverview.wox \ + UIxCalInlineMonthOverview.wox \ + UIxAppointmentView.wox \ UIxAppointmentPrintview.wox \ - UIxAppointmentEditor.wox \ - UIxCalSelectTab.wox \ - UIxCalDateLabel.wox \ - UIxCalBackForthNavView.wox \ - UIxAppointmentProposal.wox \ - UIxDatePicker.wox \ - UIxDatePickerScript.wox \ - UIxTimeSelector.wox \ - UIxTimeDateControl.wox \ - UIxCalInlineAptView.wox \ - UIxCalScheduleOverview.wox \ + UIxAppointmentEditor.wox \ + UIxCalSelectTab.wox \ + UIxCalDateLabel.wox \ + UIxCalBackForthNavView.wox \ + UIxAppointmentProposal.wox \ + UIxDatePicker.wox \ + UIxDatePickerScript.wox \ + UIxTimeSelector.wox \ + UIxTimeDateControl.wox \ + UIxCalInlineAptView.wox \ + UIxCalScheduleOverview.wox \ + UIxCalParticipationStatusView.wox \ SchedulerUI_RESOURCE_FILES += \ images/next_week.gif \ diff --git a/SOGo/UI/Scheduler/UIxAppointmentEditor.m b/SOGo/UI/Scheduler/UIxAppointmentEditor.m index f595fa67..4ed4388d 100644 --- a/SOGo/UI/Scheduler/UIxAppointmentEditor.m +++ b/SOGo/UI/Scheduler/UIxAppointmentEditor.m @@ -56,7 +56,6 @@ - (NSString *)_completeURIForMethod:(NSString *)_method; -- (iCalPerson *)getOrganizer; - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values treatAsResource:(BOOL)_isResource; @@ -65,6 +64,10 @@ - (NSString *)iCalResourcesStringFromQueryParameters; - (NSString *)iCalStringFromQueryParameter:(NSString *)_qp format:(NSString *)_format; +- (NSString *)iCalOrganizerString; + +- (id)acceptOrDeclineAction:(BOOL)_accept; + @end #include "common.h" @@ -305,14 +308,15 @@ @"BEGIN:VEVENT\r\n" @"UID:%@\r\n" @"CLASS:PUBLIC\r\n" - @"STATUS:CONFIRMED\r\n" + @"STATUS:CONFIRMED\r\n" /* confirmed by default */ @"DTSTAMP:%@\r\n" @"DTSTART:%@\r\n" @"DTEND:%@\r\n" @"TRANSP:%@\r\n" @"SEQUENCE:1\r\n" @"PRIORITY:5\r\n" - @"%@" + @"%@" /* organizer */ + @"%@" /* participants and resources */ @"END:VEVENT\r\n" @"END:VCALENDAR"; @@ -331,14 +335,15 @@ lEndDate = [lStartDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:minutes seconds:0]; - s = [self iCalParticipantsAndResourcesStringFromQueryParameters]; - template = [NSString stringWithFormat:iCalStringTemplate, - [[self clientObject] nameInContainer], - [[NSCalendarDate date] icalString], - [lStartDate icalString], - [lEndDate icalString], - [self transparency], - s]; + s = [self iCalParticipantsAndResourcesStringFromQueryParameters]; + template = [NSString stringWithFormat:iCalStringTemplate, + [[self clientObject] nameInContainer], + [[NSCalendarDate date] icalString], + [lStartDate icalString], + [lEndDate icalString], + [self transparency], + [self iCalOrganizerString], + s]; return template; } @@ -393,6 +398,27 @@ return iCalRep; } +- (NSString *)iCalOrganizerString { + static NSString *fmt = @"ORGANIZER;CN=\"%@\":mailto:%@\r\n"; + return [NSString stringWithFormat:fmt, + [self cnForUser], + [self emailForUser]]; +} + +#if 0 +- (iCalPerson *)getOrganizer { + iCalPerson *p; + NSString *emailProp; + + emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]]; + p = [[[iCalPerson alloc] init] autorelease]; + [p setEmail:emailProp]; + [p setCn:[self cnForUser]]; + return p; +} +#endif + + /* helper */ - (NSString *)_completeURIForMethod:(NSString *)_method { @@ -479,17 +505,6 @@ return date; } -- (iCalPerson *)getOrganizer { - iCalPerson *p; - NSString *emailProp; - - emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]]; - p = [[[iCalPerson alloc] init] autorelease]; - [p setEmail:emailProp]; - [p setCn:[self cnForUser]]; - return p; -} - - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values treatAsResource:(BOOL)_isResource { @@ -593,8 +608,10 @@ : lResources; } [_appointment setAttendees:attendees]; - + +#if 0 [_appointment setOrganizer:[self getOrganizer]]; +#endif } - (void)loadValuesFromICalString:(NSString *)_ical { @@ -680,8 +697,9 @@ - (id)saveAction { SOGoAppointment *apt; - NSException *ex; + iCalPerson *p; NSString *content; + NSException *ex; if (![self isWriteableClientObject]) { /* return 400 == Bad Request */ @@ -697,6 +715,11 @@ } [self saveValuesIntoAppointment:apt]; + p = [apt findParticipantWithEmail:[self emailForUser]]; + if (p) { + [p setParticipationStatus:iCalPersonPartStatAccepted]; + } + content = [apt iCalString]; [apt release]; apt = nil; @@ -714,4 +737,63 @@ return [self redirectToLocation:[self _completeURIForMethod:@".."]]; } +- (id)acceptAction { + return [self acceptOrDeclineAction:YES]; +} + +- (id)declineAction { + return [self acceptOrDeclineAction:NO]; +} + +- (id)acceptOrDeclineAction:(BOOL)_accept { + SOGoAppointment *apt; + iCalPerson *p; + NSString *iCal, *content; + NSException *ex; + + if (![self isWriteableClientObject]) { + /* 400 == Bad Request */ + return [NSException exceptionWithHTTPStatus:400 + reason:@"method cannot be invoked on " + @"the specified object"]; + } + iCal = [[self clientObject] valueForKey:@"iCalString"]; + apt = [[SOGoAppointment alloc] initWithICalString:iCal]; + if (apt == nil) { + /* 500 == Internal Server Error */ + return [NSException exceptionWithHTTPStatus:500 + reason:@"unable to parse appointment"]; + } + + p = [apt findParticipantWithEmail:[self emailForUser]]; + if (!p) { + /* 404 == Not found */ + return [NSException exceptionWithHTTPStatus:404 + reason:@"user does not participate in this " + @"appointment"]; + } + if(_accept) + [p setParticipationStatus:iCalPersonPartStatAccepted]; + else + [p setParticipationStatus:iCalPersonPartStatDeclined]; + + content = [apt iCalString]; + [apt release]; + + if (content == nil) { + /* 500 == Internal Server Error */ + return [NSException exceptionWithHTTPStatus:500 + reason:@"Could not create iCalendar data ..."]; + } + + ex = [[self clientObject] saveContentString:content]; + if (ex != nil) { + /* 500 == Internal Server Error */ + return [NSException exceptionWithHTTPStatus:500 + reason:[ex reason]]; + } + + return [self redirectToLocation:[self _completeURIForMethod:@".."]]; +} + @end /* UIxAppointmentEditor */ diff --git a/SOGo/UI/Scheduler/UIxAppointmentFormatter.h b/SOGo/UI/Scheduler/UIxAppointmentFormatter.h deleted file mode 100644 index 9a4d9f34..00000000 --- a/SOGo/UI/Scheduler/UIxAppointmentFormatter.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - 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. -*/ -// $Id$ - -#ifndef __UIxAppointmentFormatter_H__ -#define __UIxAppointmentFormatter_H__ - -#import - -/* - Formatter to format appointment-dicts to readable strings: - - %[(dateFormat)]S - - startDate formatted with the dateFormat string - - if no dateFormat is defined, the default formats are used - - !!! dont forget the ()brakes !!! - %[(dateFormat)]E - - endDate formatted with the dateFormat string - - if no dateFormat is defined, the default formats are used - - !!! dont forget the ()brakes !!! - - %[length]T - title with the specified length - - if title has more chars than length overLengthString is appended - - if no length is defined, no limit is set - - %[max]P - participants - - if more than max participants at appointment - moreParticipantsString is appended - - if no max defined no limit is set - - %[length]L - location - - if location has more chars than length overLengthString is - appended - - if no length is defined, no limit is set - - Example: - format: @"%S - %E, \n%T"; -*/ - -@class NSCalendarDate; - -@interface UIxAppointmentFormatter : NSFormatter -{ -@protected - NSString *formatString; // default: @"%S - %E, \n%T" - - NSString *dateFormat; // default: @"%H:%M" - NSString *otherDayDateFormat; // default: @"%H:%M(%m-%d)" - NSString *otherYearDateFormat; // default: @"%H:%M(%Y-%m-%d)" - - NSString *toLongString; // default: @".." - NSString *moreParticipantsString; // default: @"..." - NSString *participantsSeparator; // default: @", " - - NSCalendarDate *relationDate; // if nil, dateFormat used as format - // to know whether its the same day, the same year or another year - - BOOL showFullNames; // try to show full names of participants -} - -/* init */ - -- (id)initWithFormat:(NSString *)_format; -+ (UIxAppointmentFormatter *)formatterWithFormat:(NSString *)_format; -+ (UIxAppointmentFormatter *)formatter; - -/* accessors */ - -- (void)setFormat:(NSString *)_format; -- (NSString *)format; - -- (void)setDateFormat:(NSString *)_format; -- (NSString *)dateFormat; - -- (void)setOtherDayDateFormat:(NSString *)_format; -- (NSString *)otherDayDateFormat; - -- (void)setOtherYearDateFormat:(NSString *)_format; -- (NSString *)otherYearDateFormat; - -- (void)setToLongString:(NSString *)_toLong; -- (NSString *)toLongString; - -- (void)setMoreParticipantsString:(NSString *)_more; -- (NSString *)moreParticipantsString; - -- (void)setParticipantsSeparator:(NSString *)_sep; -- (NSString *)participantsSeparator; - -- (void)setRelationDate:(NSCalendarDate *)_relation; -- (NSCalendarDate *)relationDate; - -- (void)setShowFullNames:(BOOL)_flag; -- (BOOL)showFullNames; - -/* easy switches */ - -/* this resets the date format */ -- (void)switchToAMPMTimes:(BOOL)_showAMPM; - -@end - -#endif diff --git a/SOGo/UI/Scheduler/UIxAppointmentFormatter.m b/SOGo/UI/Scheduler/UIxAppointmentFormatter.m deleted file mode 100644 index d90a35af..00000000 --- a/SOGo/UI/Scheduler/UIxAppointmentFormatter.m +++ /dev/null @@ -1,452 +0,0 @@ -/* - 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. -*/ -// $Id$ - -#include "UIxAppointmentFormatter.h" -#import -#import -#import - -@implementation UIxAppointmentFormatter - -- (id)init { - if ((self = [super init])) { - [self setFormat:@"%S - %E, \n%T"]; - [self setDateFormat:@"%H:%M"]; - [self setOtherDayDateFormat:@"%H:%M(%m-%d)"]; - [self setOtherYearDateFormat:@"%H:%M(%Y-%m-%d)"]; - [self setToLongString:@".."]; - [self setMoreParticipantsString:@"..."]; - [self setParticipantsSeparator:@", "]; - [self setRelationDate:nil]; - self->showFullNames = NO; - } - return self; -} - -- (id)initWithFormat:(NSString *)_format { - if ((self = [self init])) { - [self setFormat:_format]; - } - return self; -} - -+ (UIxAppointmentFormatter *)formatterWithFormat:(NSString *)_format { - return AUTORELEASE([(UIxAppointmentFormatter *)[UIxAppointmentFormatter alloc] - initWithFormat:_format]); -} - -+ (UIxAppointmentFormatter *)formatter { - return AUTORELEASE([[UIxAppointmentFormatter alloc] init]); -} - -- (void)dealloc { - RELEASE(self->formatString); - RELEASE(self->dateFormat); - RELEASE(self->otherDayDateFormat); - RELEASE(self->otherYearDateFormat); - RELEASE(self->toLongString); - RELEASE(self->moreParticipantsString); - RELEASE(self->participantsSeparator); - RELEASE(self->relationDate); - - [super dealloc]; -} - -// accessors - -- (void)setFormat:(NSString *)_format { - ASSIGN(self->formatString,_format); -} -- (NSString *)format { - return self->formatString; -} - -- (void)setDateFormat:(NSString *)_format { - ASSIGN(self->dateFormat,_format); -} -- (NSString *)dateFormat { - return self->dateFormat; -} - -- (void)setOtherDayDateFormat:(NSString *)_format { - ASSIGN(self->otherDayDateFormat,_format); -} -- (NSString *)otherDayDateFormat { - return self->otherDayDateFormat; -} - -- (void)setOtherYearDateFormat:(NSString *)_format { - ASSIGN(self->otherYearDateFormat,_format); -} -- (NSString *)otherYearDateFormat { - return self->otherYearDateFormat; -} - -- (void)setToLongString:(NSString *)_toLong { - ASSIGN(self->toLongString,_toLong); -} -- (NSString *)toLongString { - return self->toLongString; -} - -- (void)setMoreParticipantsString:(NSString *)_more { - ASSIGN(self->moreParticipantsString,_more); -} -- (NSString *)moreParticipantsString { - return self->moreParticipantsString; -} - -- (void)setParticipantsSeparator:(NSString *)_sep { - ASSIGN(self->participantsSeparator,_sep); -} -- (NSString *)participantsSeparator { - return self->participantsSeparator; -} - -- (void)setRelationDate:(NSCalendarDate *)_relation { - ASSIGN(self->relationDate,_relation); -} -- (NSCalendarDate *)relationDate { - return self->relationDate; -} - -- (void)setShowFullNames:(BOOL)_flag { - self->showFullNames = _flag; -} -- (BOOL)showFullNames { - return self->showFullNames; -} - -// easy switching -- (void)switchToAMPMTimes:(BOOL)_showAMPM { - if (_showAMPM) { - [self setDateFormat:@"%I:%M %p"]; - [self setOtherDayDateFormat:@"%I:%M %p(%m-%d)"]; - [self setOtherYearDateFormat:@"%I:%M %p(%Y-%m-%d)"]; - } - else { - [self setDateFormat:@"%H:%M"]; - [self setOtherDayDateFormat:@"%H:%M(%m-%d)"]; - [self setOtherYearDateFormat:@"%H:%M(%Y-%m-%d)"]; - } -} - -// formatting helpers - -- (NSString *)formatDate:(NSCalendarDate *)_date - withFormat:(NSString *)_format -{ - NSString *f; - NSCalendarDate *rel; - - rel = self->relationDate; - - if (_format == nil) { - if (rel == nil) { - f = self->dateFormat; - } - else if ([_date isDateOnSameDay:rel]) { - f = self->dateFormat; - } - else if ([_date yearOfCommonEra] == [rel yearOfCommonEra]) { - f = self->otherDayDateFormat; - } - else { - f = self->otherYearDateFormat; - } - } - else { - f = _format; - } - return [_date descriptionWithCalendarFormat:f]; -} - -- (NSString *)formatStartDateFromApt:(id)_apt - withFormat:(NSString *)_format -{ - return [self formatDate:[_apt valueForKey:@"startDate"] - withFormat:_format]; -} - -- (NSString *)formatEndDateFromApt:(id)_apt - withFormat:(NSString *)_format -{ - return [self formatDate:[_apt valueForKey:@"endDate"] - withFormat:_format]; -} - -- (NSString *)stringForParticipant:(id)_part { - id label = nil; - - if ([[_part valueForKey:@"isTeam"] boolValue]) { - if ((label = [_part valueForKey:@"info"]) == nil) - label = [_part valueForKey:@"description"]; - } - else if (self->showFullNames) { - label = [_part valueForKey:@"firstname"]; - label = ([label length]) - ? [label stringByAppendingFormat:@" %@", [_part valueForKey:@"name"]] - : [_part valueForKey:@"name"]; - } - else if ([[_part valueForKey:@"isAccount"] boolValue]) { - label = [_part valueForKey:@"login"]; - } - else { - if ((label = [_part valueForKey:@"name"]) == nil) { - if ((label = [_part valueForKey:@"info"]) == nil) - label = [_part valueForKey:@"description"]; - } - } - - if (![label isNotNull]) - label = @"*"; - - return label; -} - -- (NSString *)participantsForApt:(id)_apt - withMaxCount:(NSString *)_cnt { - NSArray *p; - int max; - int cnt; - NSMutableString *pString; - - pString = [NSMutableString stringWithCapacity:255]; - - if (_cnt == nil) { - max = -1; // no limit - } - else { - max = [_cnt intValue]; - } - - p = [_apt valueForKey:@"participants"]; - - p = [p sortedArrayUsingKeyOrderArray: - [NSArray arrayWithObjects: - [EOSortOrdering sortOrderingWithKey:@"isAccount" - selector:EOCompareAscending], - [EOSortOrdering sortOrderingWithKey:@"login" - selector:EOCompareAscending], - nil]]; - - max = ((max > [p count]) || (max == -1)) - ? [p count] - : max; - - for (cnt = 0; cnt < max; cnt++) { - - if (cnt != 0) - [pString appendString:self->participantsSeparator]; - - [pString appendString: - [self stringForParticipant:[p objectAtIndex:cnt]]]; - } - - if (max < [p count]) { - [pString appendString:self->moreParticipantsString]; - } - - return pString; -} - -- (NSString *)titleForApt:(id)_apt withMaxLength:(NSString *)_length { - NSString *t = nil; - int l; - - l = (_length == nil) - ? -1 - : [_length intValue]; - - t = [_apt valueForKey:@"title"]; - if (!t) return @"*"; - - if (l > 1) { - if ([t length] > l) { - t = [t substringToIndex:(l - 2)]; - t = [t stringByAppendingString:self->toLongString]; - } - } - - if (l == 0) - t = @"*"; - - return t; -} - -- (NSString *)locationForApt:(id)_apt withMaxLength:(NSString *)_length { - NSString *t = nil; - int l; - - l = (_length == nil) - ? -1 - : [_length intValue]; - - t = [_apt valueForKey:@"location"]; - if (![t isNotNull] || - [t length] == 0 || - [t isEqualToString:@" "]) - return @""; - - if (l > 1) { - if ([t length] > l) { - t = [t substringToIndex:(l - 2)]; - t = [t stringByAppendingString:self->toLongString]; - } - } - - if (l == 0) - t = @""; - - return t; -} - -- (NSString *)resourcesForApt:(id)_apt withMaxLength:(NSString *)_length { - NSString *t = nil; - int l; - - l = (_length == nil) - ? -1 - : [_length intValue]; - - t = [_apt valueForKey:@"resourceNames"]; - if (![t isNotNull] || - [t length] == 0 || - [t isEqualToString:@" "]) - return @""; - - if (l > 1) { - if ([t length] > l) { - t = [t substringToIndex:(l - 2)]; - t = [t stringByAppendingString:self->toLongString]; - } - } - - if (l == 0) - t = @""; - - return t; -} - -// NSFormatter stuff - -- (NSString *)stringForObjectValue:(id)_obj { - NSMutableString *newString; - int cnt; - int length; - BOOL replaceMode = NO; - NSString *helper = nil; - NSCharacterSet *digits; - - newString = [NSMutableString stringWithCapacity:255]; - length = [self->formatString length]; - digits = [NSCharacterSet decimalDigitCharacterSet]; - - // NSLog(@"Formatting with format: %@", self->formatString); - - for (cnt = 0; cnt < length; cnt++) { - unichar c; - c = [self->formatString characterAtIndex:cnt]; - // NSLog(@"Character is: %c mode is: %@", c, - // [NSNumber numberWithBool:replaceMode]); - if (replaceMode) { - if (c == 'S') { - [newString appendString: - [self formatStartDateFromApt:_obj withFormat:helper]]; - helper = nil; - replaceMode = NO; - } - else if (c == 'E') { - [newString appendString: - [self formatEndDateFromApt:_obj withFormat:helper]]; - helper = nil; - replaceMode = NO; - } - else if (c == 'P') { - [newString appendString: - [self participantsForApt:_obj withMaxCount:helper]]; - helper = nil; - replaceMode = NO; - } - else if (c == 'T') { - [newString appendString: - [self titleForApt:_obj withMaxLength:helper]]; - helper = nil; - replaceMode = NO; - } - else if (c == 'L') { - NSString *l; - - l = [self locationForApt:_obj withMaxLength:helper]; - - if ([l length] > 0) - [newString appendString:l]; - helper = nil; - replaceMode = NO; - } - else if (c == 'R') { - NSString *r; - - r = [self resourcesForApt:_obj withMaxLength:helper]; - - if ([r length] > 0) - [newString appendString:r]; - helper = nil; - replaceMode = NO; - } - else if (c == '(') { - int end; - NSRange r = NSMakeRange(cnt,length-cnt); - - r = [self->formatString rangeOfString:@")" - options:0 range:r]; - - end = r.location - 1; - r = NSMakeRange(cnt+1, end-cnt-1); - - helper = [self->formatString substringWithRange:r]; - cnt = end + 1; - } - else if ([digits characterIsMember:c]) { - helper = (helper == nil) - ? [NSString stringWithFormat:@"%c",c] - : [NSString stringWithFormat:@"%@%c", helper, c]; - } - else { - NSLog(@"UNKNOWN FORMAT CHARACTER '%c'!!",c); - replaceMode = NO; - helper = nil; - } - } else { - if (c == '%') { - replaceMode = YES; - } - else { - [newString appendFormat:@"%c", c]; - } - } - } - - return newString; -} - -@end diff --git a/SOGo/UI/Scheduler/UIxAppointmentView.m b/SOGo/UI/Scheduler/UIxAppointmentView.m index 44f9b9b1..9346d774 100644 --- a/SOGo/UI/Scheduler/UIxAppointmentView.m +++ b/SOGo/UI/Scheduler/UIxAppointmentView.m @@ -214,20 +214,4 @@ return [self redirectToLocation:url]; } -- (id)acceptAction { - if ([self appointment] == nil) { - return [NSException exceptionWithHTTPStatus:404 - reason:@"could not locate appointment"]; - } - return self; -} - -- (id)rejectAction { - if ([self appointment] == nil) { - return [NSException exceptionWithHTTPStatus:404 - reason:@"could not locate appointment"]; - } - return self; -} - @end /* UIxAppointmentView */ diff --git a/SOGo/UI/Scheduler/UIxAppointmentView.wox b/SOGo/UI/Scheduler/UIxAppointmentView.wox index 1b15f9ef..3cff5577 100644 --- a/SOGo/UI/Scheduler/UIxAppointmentView.wox +++ b/SOGo/UI/Scheduler/UIxAppointmentView.wox @@ -214,24 +214,32 @@ + - - + - + - + + +
- + - + + +
+
- + -
+ +
diff --git a/SOGo/UI/Scheduler/UIxCalParticipationStatusView.m b/SOGo/UI/Scheduler/UIxCalParticipationStatusView.m new file mode 100644 index 00000000..a08adacf --- /dev/null +++ b/SOGo/UI/Scheduler/UIxCalParticipationStatusView.m @@ -0,0 +1,66 @@ +/* + Copyright (C) 2000-2004 SKYRIX Software AG + + This file is part of OGo + + 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. +*/ +// $Id$ + + +#include + + +@interface UIxCalParticipationStatusView : WOComponent +{ + int partStat; +} + +- (NSString *)participationStatus; + +@end + +#include /* for iCalPersonPartStat */ +#include "common.h" + +@implementation UIxCalParticipationStatusView + +- (void)setPartStat:(id)_partStat { + self->partStat = [_partStat intValue]; +} + +- (NSString *)participationStatus { + switch (self->partStat) { + case iCalPersonPartStatNeedsAction: + return @"NEEDS-ACTION"; + case iCalPersonPartStatAccepted: + return @"ACCEPTED"; + case iCalPersonPartStatDeclined: + return @"DECLINED"; + case iCalPersonPartStatTentative: + return @"TENTATIVE"; + case iCalPersonPartStatDelegated: + return @"DELEGATED"; + } + return @"OTHER"; +} + +- (NSString *)participationStatusLabel { + return [NSString stringWithFormat:@"partStat_%@", + [self participationStatus]]; +} + +@end diff --git a/SOGo/UI/Scheduler/UIxCalParticipationStatusView.wox b/SOGo/UI/Scheduler/UIxCalParticipationStatusView.wox new file mode 100644 index 00000000..c4d5d419 --- /dev/null +++ b/SOGo/UI/Scheduler/UIxCalParticipationStatusView.wox @@ -0,0 +1,8 @@ + + + diff --git a/SOGo/UI/Scheduler/UIxCalScheduleOverview.m b/SOGo/UI/Scheduler/UIxCalScheduleOverview.m index 2736ba93..06cd0b6d 100644 --- a/SOGo/UI/Scheduler/UIxCalScheduleOverview.m +++ b/SOGo/UI/Scheduler/UIxCalScheduleOverview.m @@ -28,6 +28,9 @@ NSMutableArray *userApts; NSMutableArray *foreignApts; id item; + NSArray *partNames; + NSArray *partStates; + unsigned participantIndex; } - (NSCalendarDate *)startDate; @@ -44,6 +47,12 @@ - (NSString *)appointmentBaseURL; +- (unsigned)participantsCount; +- (unsigned)maxRenderedParticipantsCount; +- (unsigned)renderedParticipantsCount; +- (unsigned)truncatedParticipantsCount; +- (BOOL)didTruncateParticipants; + @end #include @@ -56,6 +65,8 @@ - (void)dealloc { [self->userApts release]; [self->foreignApts release]; + [self->partNames release]; + [self->partStates release]; [self->item release]; [super dealloc]; } @@ -64,12 +75,30 @@ /* accessors */ - (void)setItem:(id)_item { + NSString *ps; + ASSIGN(self->item, _item); + + [self->partNames release]; + [self->partStates release]; + ps = [self->item valueForKey:@"participants"]; + self->partNames = [[ps componentsSeparatedByString:@"\n"] retain]; + ps = [self->item valueForKey:@"partstates"]; + self->partStates = [[ps componentsSeparatedByString:@"\n"] retain]; + NSLog(@"new item:%@", self->item); } - (id)item { return self->item; } +- (void)setParticipantIndex:(unsigned)_participantIndex { + NSLog(@"_participantIndex:%d", _participantIndex); + self->participantIndex = _participantIndex; +} +- (unsigned)participantIndex { + return self->participantIndex; +} + - (BOOL)hasUserAppointments { return [[self userAppointments] count] > 0; } @@ -81,15 +110,51 @@ [self hasForeignAppointments]) ? YES : NO; } -- (NSString *)participants { - NSString *s; +- (unsigned)participantsCount { + return [self->partNames count]; +} + +- (NSString *)participant { + [self logWithFormat:@"participant(%d):%@", + self->participantIndex, + [self->partNames objectAtIndex:self->participantIndex]]; + return [self->partNames objectAtIndex:self->participantIndex]; +} + +- (NSString *)participationStatus { + [self logWithFormat:@"state(%d):%@", + self->participantIndex, + [self->partStates objectAtIndex:self->participantIndex]]; + return [self->partStates objectAtIndex:self->participantIndex]; +} + +- (unsigned)maxRenderedParticipantsCount { + return 2; +} + +// TODO: find the bug in WORepetition and get rid of this! +- (NSArray *)renderedParticipants { + NSRange r; - s = [self->item valueForKey:@"participants"]; - if (!s) - return @""; - if ([s length] > 100) - s = [NSString stringWithFormat:@"%@...", [s substringToIndex:100]]; - return s; + r = NSMakeRange(0, [self renderedParticipantsCount]); + return [self->partNames subarrayWithRange:r]; +} +- (void)setFoo:(id)_foo { +} + +- (unsigned)renderedParticipantsCount { + if ([self didTruncateParticipants]) + return [self maxRenderedParticipantsCount]; + return [self participantsCount]; +} + +- (unsigned)truncatedParticipantsCount { + return [self participantsCount] - [self renderedParticipantsCount]; +} + +- (BOOL)didTruncateParticipants { + return [self participantsCount] > [self maxRenderedParticipantsCount] ? YES + : NO; } @@ -177,8 +242,8 @@ - (NSString *)acceptAppointmentURL { return [[self appointmentBaseURL] stringByAppendingPathComponent:@"accept"]; } -- (NSString *)rejectAppointmentURL { - return [[self appointmentBaseURL] stringByAppendingPathComponent:@"reject"]; +- (NSString *)declineAppointmentURL { + return [[self appointmentBaseURL] stringByAppendingPathComponent:@"decline"]; } diff --git a/SOGo/UI/Scheduler/UIxCalScheduleOverview.wox b/SOGo/UI/Scheduler/UIxCalScheduleOverview.wox index 3acd437f..57d93c6c 100644 --- a/SOGo/UI/Scheduler/UIxCalScheduleOverview.wox +++ b/SOGo/UI/Scheduler/UIxCalScheduleOverview.wox @@ -47,7 +47,9 @@ currentDate="selectedDate" > - + @@ -131,56 +162,87 @@ />
- + + + + + + + + + + + + +
+ + + +
+ + ... + +
- + + + + + + + + + + + + +
+ + + +
+ + ... + +

- + >
-

- workflow
- ========
- - in fact, the workflow we wish to implement is : - person A sets a meeting with B and C - when he sets it, he clicks on a button : either "propose" or - "propose and mail" (obvious) - In both case, when B and C logs into SOGo, they see, in their - 'news page', that a new meeting has beeing proposed - then by clicking on it, they can accept it - on the news, you have to show : meetings proposed to the person - logging in, meetings proposed BY the person logging in, - and their different acceptance - - if you reject the meeting, it still appears in the news page - as refused - - We have still two issues : ergonomic and functionnal - - The ergonomic one : I propose two sections in the news page, - with each being a table, containing, each line, an apt, - with title, day, hour, participants (truncated), the line - being green if it has been accepted by all, red if rejected - by someone - grey if in another state - - the functionnal : a meeting that is still not accepted by - everyone must appear in each participant's view, and be - counted in the conflict manager, or not ? - it's an open point -

+ +

+ workflow
+ ========
+ + in fact, the workflow we wish to implement is : + person A sets a meeting with B and C + when he sets it, he clicks on a button : either "propose" or + "propose and mail" (obvious) + In both case, when B and C logs into SOGo, they see, in their + 'news page', that a new meeting has beeing proposed + then by clicking on it, they can accept it + on the news, you have to show : meetings proposed to the person + logging in, meetings proposed BY the person logging in, + and their different acceptance + + if you reject the meeting, it still appears in the news page + as refused + + We have still two issues : ergonomic and functionnal + + The ergonomic one : I propose two sections in the news page, + with each being a table, containing, each line, an apt, + with title, day, hour, participants (truncated), the line + being green if it has been accepted by all, red if rejected + by someone + grey if in another state + + the functionnal : a meeting that is still not accepted by + everyone must appear in each participant's view, and be + counted in the conflict manager, or not ? + it's an open point +

+
diff --git a/SOGo/UI/Scheduler/UIxCalView.m b/SOGo/UI/Scheduler/UIxCalView.m index 899fd136..c1e7d0d4 100644 --- a/SOGo/UI/Scheduler/UIxCalView.m +++ b/SOGo/UI/Scheduler/UIxCalView.m @@ -2,7 +2,6 @@ #include "UIxCalView.h" #include "common.h" -#include "UIxAppointmentFormatter.h" #include #include "SoObjects/Appointments/SOGoAppointmentFolder.h" #include @@ -351,8 +350,9 @@ static BOOL shouldDisplayWeekend = NO; continue; } - res = [[self clientObject] fetchCoreInfosFromFolder:folder - from:[self startDate] to:[self endDate]]; + res = [[self clientObject] fetchOverviewInfosFromFolder:folder + from:[self startDate] + to:[self endDate]]; if (res == nil) { [self errorWithFormat:@"fetch failed for user: %@", uid]; continue; @@ -388,7 +388,8 @@ static BOOL shouldDisplayWeekend = NO; aptFolder = [self clientObject]; self->appointments = - [[aptFolder fetchCoreInfosFrom:[self startDate] to:[self endDate]] retain]; + [[aptFolder fetchOverviewInfosFrom:[self startDate] + to:[self endDate]] retain]; uids = [[[[self context] request] formValueForKey:@"uids"] stringValue]; uids = [uids length] > 0 ? [uids componentsSeparatedByString:@","] : nil; diff --git a/SOGo/UI/Scheduler/Version b/SOGo/UI/Scheduler/Version index 4405f93a..07748cc2 100644 --- a/SOGo/UI/Scheduler/Version +++ b/SOGo/UI/Scheduler/Version @@ -1,6 +1,6 @@ # $Id$ -SUBMINOR_VERSION:=103 +SUBMINOR_VERSION:=104 # v0.9.101 requires NGiCal v4.5.36 # v0.9.100 requires SOGoUI v0.9.21 diff --git a/SOGo/UI/Scheduler/product.plist b/SOGo/UI/Scheduler/product.plist index c7496614..62fd572e 100644 --- a/SOGo/UI/Scheduler/product.plist +++ b/SOGo/UI/Scheduler/product.plist @@ -118,24 +118,24 @@ pageName = "UIxAppointmentView"; actionName = "delete"; }; - accept = { + edit = { protectedBy = "View"; - pageName = "UIxAppointmentView"; - actionName = "accept"; + pageName = "UIxAppointmentEditor"; }; - reject = { + save = { protectedBy = "View"; - pageName = "UIxAppointmentView"; - actionName = "reject"; + pageName = "UIxAppointmentEditor"; + actionName = "save"; }; - edit = { + accept = { protectedBy = "View"; pageName = "UIxAppointmentEditor"; + actionName = "accept"; }; - save = { + decline = { protectedBy = "View"; pageName = "UIxAppointmentEditor"; - actionName = "save"; + actionName = "decline"; }; test = { protectedBy = "View"; diff --git a/SOGoLogic/ChangeLog b/SOGoLogic/ChangeLog index fa432734..24f8b6b2 100644 --- a/SOGoLogic/ChangeLog +++ b/SOGoLogic/ChangeLog @@ -1,3 +1,12 @@ +2004-12-17 Marcus Mueller + + * v0.9.32 + + * SOGoAppointmentICalRenderer.m: add correct participant state + + * SOGoAppointment.[hm]: find participants with specific email + address. + 2004-12-13 Marcus Mueller * SOGoAppointment.m: removed UIx_Privates declaration (no longer diff --git a/SOGoLogic/SOGoAppointment.h b/SOGoLogic/SOGoAppointment.h index f358d921..774dd757 100644 --- a/SOGoLogic/SOGoAppointment.h +++ b/SOGoLogic/SOGoAppointment.h @@ -117,7 +117,11 @@ - (BOOL)isOrganizer:(id)_email; - (BOOL)isParticipant:(id)_email; - + +/* searching */ + +- (iCalPerson *)findParticipantWithEmail:(id)_email; + @end #endif /* __SOGoAppointment_H_ */ diff --git a/SOGoLogic/SOGoAppointment.m b/SOGoLogic/SOGoAppointment.m index 06c7c116..802e89aa 100644 --- a/SOGoLogic/SOGoAppointment.m +++ b/SOGoLogic/SOGoAppointment.m @@ -373,6 +373,23 @@ static NGLogger *logger = nil; 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 */ +} + /* description */ - (void)appendAttributesToDescription:(NSMutableString *)_ms { diff --git a/SOGoLogic/SOGoAppointmentICalRenderer.m b/SOGoLogic/SOGoAppointmentICalRenderer.m index 32e0739d..ecba50da 100644 --- a/SOGoLogic/SOGoAppointmentICalRenderer.m +++ b/SOGoLogic/SOGoAppointmentICalRenderer.m @@ -103,8 +103,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:@"\""]; diff --git a/SOGoLogic/Version b/SOGoLogic/Version index 6a99300f..2ab98926 100644 --- a/SOGoLogic/Version +++ b/SOGoLogic/Version @@ -1,7 +1,8 @@ # Version file -SUBMINOR_VERSION:=31 +SUBMINOR_VERSION:=32 +# v0.9.32 requires NGiCal v4.5.37 # v0.9.31 requires NGiCal v4.5.36 # v0.9.26 requires NGExtensions v4.5.136 # v0.9.23 requires NGiCal v4.3.32 -- 2.39.5