diff --git a/ChangeLog b/ChangeLog index 4d4d1db44..e8ca12734 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,86 @@ refresh the tasks list when receiving the AJAX response instead of emptying the list before sending the AJAX request. +2010-05-05 Wolfgang Sourdeau + + * UI/WebServerResources/UIxAttendeesEditor.js (onContactKeydown): + the code for the "enter" and "tab" has been merged, since it was + similar. We also resolve individual contacts from list entries. + (resolveListAttendees): new function that fetches the indivual + entries from contacts lists. + (resolveListAttendeesCallback): callback for the above, that + create rows and input fields corresponding to the returned + entries, and trigger a contact search on each of them. + (performSearch): take an "input" field as argument since + "attendeesEditor.currentField" is no longer used. + (performSearchCallbacks): roles and partstats are now stored in + individual attributes rather than in the element class. We also + now behave differently depending on whether the event owner is + returned or another type of user, in order to match Lightning's + behaviour. + (newAttendee): no longer a callback, and now takes an optional + argument that indicates with attendee row precedes the one being + created. Also the new row is returned. + (checkAttendee): no longer a callback. Now takes an "input" + argument. + (onInputBlur): new callback that invokes "checkAttendee" and + handle the cleanup of the entries menu. + (displayFreeBusyForNode): use DOM methods rather than "innerHTML" + to modify the cell contents. + (updateFreeBusyDataCallback): freebusy requests are no longer + sequential. + (prepareAttendees): we now modify the DOM attributes rather than + the HTML attributes. + + * UI/Contacts/UIxListView.m (-propertiesAction): we now invoke + -[self responseWithStatus:andJSONRepresentation:] to build the + resulting WOResponse. + + * UI/Contacts/UIxContactFoldersView.m (-allContactSearchAction): + the type of returned component can now be deduced from the value + of "c_component" rather than from the c_name extension... Also, we + avoid autoreleasing variables where it's not needed. + (-contactSearchAction): removed obsolete method. + + * SoObjects/Contacts/SOGoContactGCSFolder.m (+initialize): new + method initializing "folderListingFields" as a static NSArray + rather than as macro. Added "c_component" to the list of fields to + retrieve. + + * UI/Scheduler/UIxComponentEditor.m (-ownerLogin): new accessor + the differenciate between the type of users in the list of + attendees. + + * UI/WebServerResources/UIxAttendeesEditor.js + (performSearchCallback): differenciate between the organizer user + and other attendees, as in Lightning. + + * UI/WebServerResources/UIxAppointmentEditor.js + (setupAttendeeNode): removed useless calls to $(). + + * UI/Scheduler/UIxComponentEditor.m + (-ownerIsAttendee:andClientObject:) + (delegateIsAttendee:andClientObject:): handle the case where the + user is found but has no RSVP or one with "FALSE" as value. + (-userHasRSVP): renamed from "userIsAttendee", since we don't + enable users without a RSVP set as "TRUE" to repond to + invitations. + (-currentAttendeeClasses): new method. + + * UI/MailPartViewers/UIxMailPartICalActions.m (-tentativeAction): + new action method for the "TENTATIVE" partstat. + + * SoObjects/Appointments/iCalEntityObject+SOGo.m + (-userIsParticipant:): renamed to "userIsAttendee:". We now + request the list of attendees rather than the list of + participants. + (-userAsParticipant:): renamed to "userAsAttendee:". Again, we + return the attendee matching the user, whether he/she is a + participant or not. + + * SoObjects/SOGo/iCalEntityObject+Utilities.[hm]: removed useless + module, since it implemented methods already found elsewhere. + 2010-05-04 Wolfgang Sourdeau * UI/WebServerResources/ContactsUI.js (initContacts): we must diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index 9776cecca..20ec67b1f 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,16 @@ +2010-05-05 Wolfgang Sourdeau + + * iCalPerson.m (-setParticipationStatus:, -participationStatus): + added handling of the new partstat value + "iCalPersonPartStatUndefined". + + * iCalEntityObject.m (-resources): removed useless method. + (-nonParticipants): new method returning ATTENDEE objects having + their ROLE attribute set to "NON-PARTICIPANT". + (-isAttendee:): new method. + (-findAttendeeWithEmail): new method replacing + "findParticipantWithEmail:". + 2010-04-28 Wolfgang Sourdeau * iCalRecurrenceRule.m (-iCalRepresentationForWeekDay:): made @@ -34,7 +47,7 @@ * iCalWeeklyRecurrenceCalculator.m (-recurrenceRangesWithinCalendarDateRange:): make use of the new - iCalByDayMask class. + iCalByDayMask class. (-lastInstanceStartDate): make use of the previous method when a BYxxx mask is defined in the rule. diff --git a/SOPE/NGCards/iCalEntityObject.h b/SOPE/NGCards/iCalEntityObject.h index ba62d1ce9..36491e206 100644 --- a/SOPE/NGCards/iCalEntityObject.h +++ b/SOPE/NGCards/iCalEntityObject.h @@ -109,15 +109,16 @@ typedef enum - (void) addToAttendees: (iCalPerson *) _person; - (NSArray *) attendees; - (void) setAttendees: (NSArray *) attendees; +- (BOOL) isAttendee: (id) _email; - (void) removeFromAttendees: (iCalPerson *) oldAttendee; - (void) removeAllAttendees; -/* categorize attendees into participants and resources */ +/* categorize attendees into participants and non-participants */ - (NSArray *) participants; -- (NSArray *) resources; +- (NSArray *) nonParticipants; - (BOOL) isParticipant: (id) _email; -- (iCalPerson *) findParticipantWithEmail: (id) _email; +- (iCalPerson *) findAttendeeWithEmail: (id) email; - (void) removeAllAlarms; - (void) addToAlarms: (id) _alarm; diff --git a/SOPE/NGCards/iCalEntityObject.m b/SOPE/NGCards/iCalEntityObject.m index 11d7f82dc..bbe7fa262 100644 --- a/SOPE/NGCards/iCalEntityObject.m +++ b/SOPE/NGCards/iCalEntityObject.m @@ -330,6 +330,40 @@ return [self childrenWithTag: @"attendee"]; } +- (BOOL) isAttendee: (id) _email +{ + NSArray *attEmails; + + _email = [_email lowercaseString]; + attEmails = [[self attendees] valueForKey:@"rfc822Email"]; + attEmails = [attEmails valueForKey: @"lowercaseString"]; + return [attEmails containsObject:_email]; +} + +- (iCalPerson *) findAttendeeWithEmail: (id) email +{ + NSArray *attendees; + unsigned int count, max; + NSString *lowerEmail, *currentEmail; + iCalPerson *attendee, *currentAttendee; + + attendee = nil; + + lowerEmail = [email lowercaseString]; + attendees = [self attendees]; + max = [attendees count]; + + for (count = 0; attendee == nil && count < max; count++) + { + currentAttendee = [attendees objectAtIndex: count]; + currentEmail = [[currentAttendee rfc822Email] lowercaseString]; + if ([currentEmail isEqualToString: lowerEmail]) + attendee = currentAttendee; + } + + return attendee; +} + - (void) removeAllAlarms { [children removeObjectsInArray: [self alarms]]; @@ -406,48 +440,6 @@ } /* stuff */ - -- (NSArray *) participants -{ - return [self _filteredAttendeesThinkingOfPersons: YES]; -} - -- (NSArray *) resources -{ - return [self _filteredAttendeesThinkingOfPersons: NO]; -} - -- (NSArray *) _filteredAttendeesThinkingOfPersons: (BOOL) _persons -{ - NSArray *list; - NSMutableArray *filtered; - unsigned count, max; - iCalPerson *person; - NSString *role; - - if (_persons) - { - list = [self attendees]; - max = [list count]; - filtered = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - person = (iCalPerson *) [list objectAtIndex: count]; - role = [[person role] uppercaseString]; - if (![role hasPrefix: @"NON-PART"]) - [filtered addObject: person]; - } - - list = filtered; - } - else - list = [self childrenWithTag: @"attendee" - andAttribute: @"role" - havingValue: @"non-part"]; - - return list; -} - - (BOOL) isOrganizer: (id) _email { NSString *organizerMail; @@ -458,6 +450,35 @@ isEqualToString: [_email lowercaseString]]; } +- (NSArray *) participants +{ + NSArray *list; + NSMutableArray *filtered; + unsigned count, max; + iCalPerson *person; + NSString *role; + + list = [self attendees]; + max = [list count]; + filtered = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + person = [list objectAtIndex: count]; + role = [[person role] uppercaseString]; + if (![role hasPrefix: @"NON-PARTICIPANT"]) + [filtered addObject: person]; + } + + return filtered; +} + +- (NSArray *) nonParticipants +{ + return [self childrenWithTag: @"attendee" + andAttribute: @"role" + havingValue: @"non-participant"]; +} + - (BOOL) isParticipant: (id) _email { NSArray *partEmails; @@ -468,26 +489,6 @@ return [partEmails containsObject:_email]; } -- (iCalPerson *) findParticipantWithEmail: (id) _email -{ - NSArray *ps; - unsigned i, count; - - _email = [_email lowercaseString]; - ps = [self participants]; - count = [ps count]; - - for (i = 0; i < count; i++) { - iCalPerson *p; - - p = [ps objectAtIndex:i]; - if ([[[p rfc822Email] lowercaseString] isEqualToString:_email]) - return p; - } - - return nil; /* not found */ -} - - (NSComparisonResult) _compareValue: (id) selfValue withValue: (id) otherValue { diff --git a/SOPE/NGCards/iCalPerson.h b/SOPE/NGCards/iCalPerson.h index 6c7644673..4aac91d14 100644 --- a/SOPE/NGCards/iCalPerson.h +++ b/SOPE/NGCards/iCalPerson.h @@ -25,6 +25,7 @@ #import "CardElement.h" typedef enum { + iCalPersonPartStatUndefined = -1, /* empty/undefined */ iCalPersonPartStatNeedsAction = 0, /* NEEDS-ACTION (DEFAULT) */ iCalPersonPartStatAccepted = 1, /* ACCEPTED */ iCalPersonPartStatDeclined = 2, /* DECLINED */ diff --git a/SOPE/NGCards/iCalPerson.m b/SOPE/NGCards/iCalPerson.m index 971a0d101..449c1b012 100644 --- a/SOPE/NGCards/iCalPerson.m +++ b/SOPE/NGCards/iCalPerson.m @@ -133,6 +133,9 @@ NSString *stat; switch (_status) { + case iCalPersonPartStatUndefined: + stat = @""; + break; case iCalPersonPartStatAccepted: stat = @"ACCEPTED"; break; @@ -170,7 +173,9 @@ NSString *stat; stat = [[self partStat] uppercaseString]; - if (![stat length] || [stat isEqualToString:@"NEEDS-ACTION"]) + if (![stat length]) + return iCalPersonPartStatUndefined; + else if ([stat isEqualToString:@"NEEDS-ACTION"]) return iCalPersonPartStatNeedsAction; else if ([stat isEqualToString:@"ACCEPTED"]) return iCalPersonPartStatAccepted; diff --git a/SOPE/NGCards/versitCardsSaxDriver/ChangeLog b/SOPE/NGCards/versitCardsSaxDriver/ChangeLog index 27c495fd6..5afb1fde4 100644 --- a/SOPE/NGCards/versitCardsSaxDriver/ChangeLog +++ b/SOPE/NGCards/versitCardsSaxDriver/ChangeLog @@ -1,3 +1,8 @@ +2010-05-05 Wolfgang Sourdeau + + * VSSaxDriver.m (_endComponent:value:): avoid a crash occurring + when an inconsistency is found in the stack of containers. + 2009-12-22 Wolfgang Sourdeau * VSSaxDriver.m (_endComponent:value:): worked-around the lameness diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m index 7da82eb50..4b6763941 100644 --- a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m +++ b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m @@ -31,6 +31,7 @@ #import "VSSaxDriver.h" #import #import +#import #import #import #import "common.h" @@ -653,6 +654,7 @@ static NSCharacterSet *whitespaceCharSet = nil; value: (NSString *) tagValue { NSString *mtName; + int max; mtName = [[self _mapTagName: tagValue] uppercaseString]; if ([cardStack count] > 0) @@ -692,11 +694,19 @@ static NSCharacterSet *whitespaceCharSet = nil; stringByAppendingString: mtName]]; } [self _endGroupElementTag: mtName]; - [cardStack removeLastObject]; - + + max = [cardStack count]; + if (max > 0) + { + [cardStack removeLastObject]; + max--; + } + else + [self errorWithFormat: @"serious inconsistency among begin/end tags"]; + /* report parsed elements */ - - if ([cardStack count] == 0) + + if (max == 0) [self reportQueuedTags]; } diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index a143f11ef..896c23d2e 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -39,7 +39,6 @@ #import -#import #import #import #import @@ -591,13 +590,13 @@ SOGoUser *currentUser; currentUser = [context activeUser]; - otherAttendee = [event findParticipant: theOwnerUser]; + otherAttendee = [event userAsAttendee: theOwnerUser]; delegateEmail = [otherAttendee delegatedTo]; if ([delegateEmail length]) delegateEmail = [delegateEmail rfc822Email]; if ([delegateEmail length]) - otherDelegate = [event findParticipantWithEmail: delegateEmail]; + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else otherDelegate = NO; @@ -635,7 +634,7 @@ delegateEmail = [delegateEmail rfc822Email]; if ([delegateEmail length]) - otherDelegate = [event findParticipantWithEmail: delegateEmail]; + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else otherDelegate = NO; } @@ -708,7 +707,7 @@ delegateEmail = [delegateEmail rfc822Email]; if ([delegateEmail length]) - otherDelegate = [event findParticipantWithEmail: delegateEmail]; + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else otherDelegate = NO; @@ -792,7 +791,7 @@ delegateEmail = [delegateEmail rfc822Email]; if ([delegateEmail length]) - otherDelegate = [event findParticipantWithEmail: delegateEmail]; + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else otherDelegate = NO; } @@ -860,7 +859,7 @@ shouldAddSentBy: YES]; } - // We update the calendar of all participants that are + // We update the calendar of all attendees that are // local to the system. This is useful in case user A accepts // invitation from organizer B and users C, D, E who are also // attendees need to verify if A has accepted. @@ -1075,7 +1074,7 @@ [self sendReceiptEmailUsingTemplateNamed: (isUpdate ? @"Update" : @"Invitation") forObject: emailEvent - to: [newEvent participants]]; + to: [newEvent attendees]]; return elements; } @@ -1120,7 +1119,7 @@ [self sendReceiptEmailUsingTemplateNamed: @"Deletion" forObject: event - to: [event participants]]; + to: [event attendees]]; return elements; } @@ -1161,7 +1160,7 @@ } // Find attendee within event - localAttendee = [event findParticipantWithEmail: [attendee rfc822Email]]; + localAttendee = [event findAttendeeWithEmail: [attendee rfc822Email]]; if (localAttendee) { // Update the attendee's status @@ -1230,7 +1229,7 @@ [[event parent] setMethod: @""]; ownerUser = [SOGoUser userWithLogin: [[SOGoUserManager sharedUserManager] getUIDForEmail: originator]]; - attendee = [event findParticipant: ownerUser]; + attendee = [event userAsAttendee: ownerUser]; eventUID = [event uid]; delegate = nil; @@ -1240,7 +1239,7 @@ delegateEmail = [delegateEmail substringFromIndex: 7]; if ([delegateEmail length]) delegate - = [event findParticipantWithEmail: delegateEmail]; + = [event findAttendeeWithEmail: delegateEmail]; } recipientsEnum = [recipients objectEnumerator]; @@ -1326,7 +1325,7 @@ // change will be on the attendee corresponding to the ownerUser. ownerUser = [SOGoUser userWithLogin: owner]; - attendee = [event findParticipant: ownerUser]; + attendee = [event userAsAttendee: ownerUser]; if (attendee) { if (delegate @@ -1338,9 +1337,9 @@ if (delegatedUser != nil && [event userIsOrganizer: delegatedUser]) ex = [NSException exceptionWithHTTPStatus: 403 reason: @"delegate is organizer"]; - if ([event isParticipant: [[delegate email] rfc822Email]]) + if ([event isAttendee: [[delegate email] rfc822Email]]) ex = [NSException exceptionWithHTTPStatus: 403 - reason: @"delegate is a participant"]; + reason: @"delegate is a attendee"]; else if ([SOGoGroup groupWithEmail: [[delegate email] rfc822Email] inDomain: [ownerUser domain]]) ex = [NSException exceptionWithHTTPStatus: 403 @@ -1411,7 +1410,7 @@ to: attendees]; } } - else if ([occurence userIsParticipant: ownerUser]) + else if ([occurence userIsAttendee: ownerUser]) // The current user deletes the occurence; let the organizer know that // the user has declined this occurence. [self changeParticipationStatus: @"DECLINED" withDelegate: nil @@ -1439,7 +1438,7 @@ NSArray *allEvents; int count, max; iCalEvent *currentEvent; - iCalPerson *ownerParticipant; + iCalPerson *ownerAttendee; NSString *key; SOGoUser *ownerUser; @@ -1452,14 +1451,14 @@ for (count = 0; count < max; count++) { currentEvent = [allEvents objectAtIndex: count]; - ownerParticipant = [currentEvent userAsParticipant: ownerUser]; - if (ownerParticipant) + ownerAttendee = [currentEvent userAsAttendee: ownerUser]; + if (ownerAttendee) { if (count == 0) key = @"master"; else key = [[currentEvent recurrenceId] iCalFormattedDateTimeString]; - [partStats setObject: ownerParticipant forKey: key]; + [partStats setObject: ownerAttendee forKey: key]; } } diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index b20668814..9f0b810e5 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -41,7 +41,6 @@ #import #import -#import #import #import #import @@ -825,7 +824,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, p = [app pageWithName: pageName inContext: context]; [p setApt: (iCalEvent *) event]; - attendee = [event findParticipant: from]; + attendee = [event userAsAttendee: from]; [p setAttendee: attendee]; /* construct message */ @@ -897,7 +896,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, if (![event userIsOrganizer: ownerUser]) { organizer = [event organizer]; - attendee = [event findParticipant: ownerUser]; + attendee = [event userAsAttendee: ownerUser]; [event setAttendees: [NSArray arrayWithObject: attendee]]; [self sendIMIPReplyForEvent: event from: from to: organizer]; } @@ -981,7 +980,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, user = [SOGoUser userWithLogin: uid]; component = [self component: NO secure: NO]; - return [component findParticipant: user]; + return [component userAsAttendee: user]; } - (iCalPerson *) iCalPersonWithUID: (NSString *) uid @@ -1061,7 +1060,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, ownerUser = [SOGoUser userWithLogin: owner]; if ([component userIsOrganizer: ownerUser]) role = SOGoCalendarRole_Organizer; - else if ([component userIsParticipant: ownerUser]) + else if ([component userIsAttendee: ownerUser]) role = SOGoCalendarRole_Participant; else role = SOGoRole_None; @@ -1119,7 +1118,7 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence, aclUser = [SOGoUser userWithLogin: uid]; if ([component userIsOrganizer: aclUser]) [roles addObject: SOGoCalendarRole_Organizer]; - else if ([component userIsParticipant: aclUser]) + else if ([component userIsAttendee: aclUser]) [roles addObject: SOGoCalendarRole_Participant]; accessRole = [container roleForComponentsWithAccessClass: [component symbolicAccessClass] diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h index 6d52d9e5d..6d1545dcc 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -34,10 +34,10 @@ extern NSNumber *iCalDistantFutureNumber; + (void) initializeSOGoExtensions; -- (BOOL) userIsParticipant: (SOGoUser *) user; +- (BOOL) userIsAttendee: (SOGoUser *) user; - (BOOL) userIsOrganizer: (SOGoUser *) user; -- (iCalPerson *) userAsParticipant: (SOGoUser *) user; +- (iCalPerson *) userAsAttendee: (SOGoUser *) user; - (NSArray *) attendeeUIDs; - (BOOL) isStillRelevant; diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index fe5fac74b..b5d21f6a4 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -57,40 +57,40 @@ NSNumber *iCalDistantFutureNumber = nil; } } -- (BOOL) userIsParticipant: (SOGoUser *) user +- (BOOL) userIsAttendee: (SOGoUser *) user { - NSEnumerator *participants; - iCalPerson *currentParticipant; - BOOL isParticipant; + NSEnumerator *attendees; + iCalPerson *currentAttendee; + BOOL isAttendee; - isParticipant = NO; + isAttendee = NO; - participants = [[self participants] objectEnumerator]; - currentParticipant = [participants nextObject]; - while (!isParticipant - && currentParticipant) - if ([user hasEmail: [currentParticipant rfc822Email]]) - isParticipant = YES; + attendees = [[self attendees] objectEnumerator]; + currentAttendee = [attendees nextObject]; + while (!isAttendee + && currentAttendee) + if ([user hasEmail: [currentAttendee rfc822Email]]) + isAttendee = YES; else - currentParticipant = [participants nextObject]; + currentAttendee = [attendees nextObject]; - return isParticipant; + return isAttendee; } -- (iCalPerson *) userAsParticipant: (SOGoUser *) user +- (iCalPerson *) userAsAttendee: (SOGoUser *) user { - NSEnumerator *participants; - iCalPerson *currentParticipant, *userParticipant; + NSEnumerator *attendees; + iCalPerson *currentAttendee, *userAttendee; - userParticipant = nil; + userAttendee = nil; - participants = [[self participants] objectEnumerator]; - while (!userParticipant - && (currentParticipant = [participants nextObject])) - if ([user hasEmail: [currentParticipant rfc822Email]]) - userParticipant = currentParticipant; + attendees = [[self attendees] objectEnumerator]; + while (!userAttendee + && (currentAttendee = [attendees nextObject])) + if ([user hasEmail: [currentAttendee rfc822Email]]) + userAttendee = currentAttendee; - return userParticipant; + return userAttendee; } - (BOOL) userIsOrganizer: (SOGoUser *) user diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 7c75371e9..1fa3f8f9e 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -46,13 +46,20 @@ #import "SOGoContactGCSFolder.h" -#define folderListingFields [NSArray arrayWithObjects: @"c_name", @"c_cn", \ - @"c_givenname", @"c_sn", @"c_screenname", \ - @"c_o", @"c_mail", @"c_telephonenumber", \ - nil] +static NSArray *folderListingFields = nil; @implementation SOGoContactGCSFolder ++ (void) initialize +{ + if (!folderListingFields) + folderListingFields = [[NSArray alloc] initWithObjects: @"c_name", + @"c_cn", @"c_givenname", @"c_sn", + @"c_screenname", @"c_o", + @"c_mail", @"c_telephonenumber", + @"c_component", nil]; +} + - (Class) objectClassForContent: (NSString *) content { CardGroup *cardEntry; diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index ac5e0dcaa..58c0970c6 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -34,7 +34,6 @@ SOGo_HEADER_FILES = \ SOGoDateFormatter.h \ SOGoPermissions.h \ SOGoStartupLogger.h \ - iCalEntityObject+Utilities.h \ NSArray+DAV.h \ NSArray+Utilities.h \ NSCalendarDate+SOGo.h \ @@ -103,7 +102,6 @@ SOGo_OBJC_FILES = \ SQLSource.m \ SOGoUserProfile.m \ SOGoSQLUserProfile.m \ - iCalEntityObject+Utilities.m \ NSArray+DAV.m \ NSArray+Utilities.m \ NSCalendarDate+SOGo.m \ diff --git a/SoObjects/SOGo/iCalEntityObject+Utilities.h b/SoObjects/SOGo/iCalEntityObject+Utilities.h deleted file mode 100644 index c60441b59..000000000 --- a/SoObjects/SOGo/iCalEntityObject+Utilities.h +++ /dev/null @@ -1,37 +0,0 @@ -/* iCalEntityObject+Utilities.h - this file is part of SOGo - * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef ICALENTITYOBJECT_UTILITIES_H -#define ICALENTITYOBJECT_UTILITIES_H - -#import - -@class iCalPerson; -@class SOGoUser; - -@interface iCalEntityObject (SOGoAddition) - -- (iCalPerson *) findParticipant: (SOGoUser *) user; - -@end - -#endif /* ICALENTITYOBJECT_UTILITIES_H */ diff --git a/SoObjects/SOGo/iCalEntityObject+Utilities.m b/SoObjects/SOGo/iCalEntityObject+Utilities.m deleted file mode 100644 index a23594ed7..000000000 --- a/SoObjects/SOGo/iCalEntityObject+Utilities.m +++ /dev/null @@ -1,53 +0,0 @@ -/* iCalEntityObject+Utilities.m - this file is part of SOGo - * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import - -#import - -#import "SOGoUser.h" - -#import "iCalEntityObject+Utilities.h" - -#warning we should move this into Appointments. - -@implementation iCalEntityObject (SOGoAddition) - -- (iCalPerson *) findParticipant: (SOGoUser *) user -{ - iCalPerson *participant, *currentParticipant; - NSEnumerator *participants; - - participant = nil; - participants = [[self attendees] objectEnumerator]; - currentParticipant = [participants nextObject]; - while (currentParticipant && !participant) - if ([user hasEmail: [currentParticipant rfc822Email]]) - participant = currentParticipant; - else - currentParticipant = [participants nextObject]; - - return participant; -} - -@end diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index fb17c312a..525a7044a 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -78,8 +78,11 @@ us = [activeUser userSettings]; moduleSettings = [us objectForKey: module]; if (!moduleSettings) - moduleSettings = [NSMutableDictionary dictionary]; - [us setObject: moduleSettings forKey: module]; + { + moduleSettings = [NSMutableDictionary new]; + [us setObject: moduleSettings forKey: module]; + [moduleSettings release]; + } contextIsSetup = YES; } } @@ -149,44 +152,6 @@ return email; } -- (NSArray *) _responseForResults: (NSArray *) results -{ - NSEnumerator *contacts; - NSString *email, *info; - NSDictionary *contact; - NSMutableArray *formattedContacts; - NSMutableDictionary *formattedContact; - - formattedContacts = [NSMutableArray arrayWithCapacity: [results count]]; - if ([results count] > 0) - { - contacts = [results objectEnumerator]; - contact = [contacts nextObject]; - while (contact) - { - email = [contact objectForKey: @"c_email"]; - if ([email length]) - { - formattedContact = [NSMutableDictionary dictionary]; - [formattedContact setObject: [contact objectForKey: @"c_uid"] - forKey: @"uid"]; - [formattedContact setObject: [contact objectForKey: @"cn"] - forKey: @"name"]; - [formattedContact setObject: email - forKey: @"email"]; - info = [contact objectForKey: @"c_info"]; - if (info != nil) - [formattedContact setObject: info - forKey: @"contactInfo"]; - [formattedContacts addObject: formattedContact]; - } - contact = [contacts nextObject]; - } - } - - return formattedContacts; -} - - (id ) allContactSearchAction { id result; @@ -196,7 +161,7 @@ NSArray *folders, *contacts, *descriptors, *sortedContacts; NSMutableArray *sortedFolders; NSMutableDictionary *contact, *uniqueContacts; - unsigned int i, j; + unsigned int i, j, max; NSSortDescriptor *commonNameDescriptor; BOOL excludeGroups, excludeLists; @@ -217,9 +182,10 @@ else [localException raise]; NS_ENDHANDLER; - sortedFolders = [NSMutableArray arrayWithCapacity: [folders count]]; + max = [folders count]; + sortedFolders = [NSMutableArray arrayWithCapacity: max]; uniqueContacts = [NSMutableDictionary dictionary]; - for (i = 0; i < [folders count]; i++) + for (i = 0; i < max; i++) { folder = [folders objectAtIndex: i]; /* We first search in LDAP folders (in case of duplicated entries in GCS folders) */ @@ -228,7 +194,7 @@ else [sortedFolders addObject: folder]; } - for (i = 0; i < [sortedFolders count]; i++) + for (i = 0; i < max; i++) { folder = [sortedFolders objectAtIndex: i]; //NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]); @@ -245,8 +211,8 @@ && [uniqueContacts objectForKey: mail] == nil && !(excludeGroups && [contact objectForKey: @"isGroup"])) [uniqueContacts setObject: contact forKey: mail]; - else if (!excludeLists - && [[contact objectForKey: @"c_name"] hasSuffix: @".vlf"]) + else if (!excludeLists && [[contact objectForKey: @"c_component"] + isEqualToString: @"vlist"]) { [contact setObject: [folder nameInContainer] forKey: @"container"]; @@ -254,14 +220,16 @@ forKey: [contact objectForKey: @"c_name"]]; } } - } + } if ([uniqueContacts count] > 0) { // Sort the contacts by display name - commonNameDescriptor = [[[NSSortDescriptor alloc] initWithKey: @"c_cn" - ascending:YES] autorelease]; + commonNameDescriptor = [[NSSortDescriptor alloc] initWithKey: @"c_cn" + ascending:YES]; descriptors = [NSArray arrayWithObjects: commonNameDescriptor, nil]; - sortedContacts = [[uniqueContacts allValues] sortedArrayUsingDescriptors: descriptors]; + [commonNameDescriptor release]; + sortedContacts = [[uniqueContacts allValues] + sortedArrayUsingDescriptors: descriptors]; } else sortedContacts = [NSArray array]; @@ -269,7 +237,7 @@ sortedContacts, @"contacts", nil]; result = [self responseWithStatus: 200]; - [(WOResponse*)result appendContentString: [data jsonRepresentation]]; + [(WOResponse*) result appendContentString: [data jsonRepresentation]]; } else result = [NSException exceptionWithHTTPStatus: 400 @@ -278,36 +246,6 @@ return result; } -- (id ) contactSearchAction -{ - NSDictionary *data; - NSArray *contacts; - NSString *searchText, *domain; - id result; - SOGoUserManager *um; - - searchText = [self queryParameterForKey: @"search"]; - if ([searchText length] > 0) - { - um = [SOGoUserManager sharedUserManager]; - domain = [[context activeUser] domain]; - contacts - = [self _responseForResults: [um fetchContactsMatching: searchText - inDomain: domain]]; - data = [NSDictionary dictionaryWithObjectsAndKeys: - searchText, @"searchText", - contacts, @"contacts", - nil]; - result = [self responseWithStatus: 200]; - [(WOResponse*)result appendContentString: [data jsonRepresentation]]; - } - else - result = [NSException exceptionWithHTTPStatus: 400 - reason: @"missing 'search' parameter"]; - - return result; -} - - (NSArray *) _subFoldersFromFolder: (SOGoParentFolder *) parentFolder { NSMutableArray *folders; @@ -375,8 +313,7 @@ - (NSString *) currentContactFolderId { - return [NSString stringWithFormat: @"/%@", - [currentFolder nameInContainer]]; + return [NSString stringWithFormat: @"/%@", [currentFolder nameInContainer]]; } - (NSString *) currentContactFolderName @@ -391,7 +328,8 @@ - (NSString *) currentContactFolderClass { - return ([currentFolder isKindOfClass: [SOGoContactSourceFolder class]]? @"remote" : @"local"); + return ([currentFolder isKindOfClass: [SOGoContactSourceFolder class]] + ? @"remote" : @"local"); } - (NSString *) verticalDragHandleStyle @@ -401,7 +339,8 @@ [self _setupContext]; vertical = [moduleSettings objectForKey: @"DragHandleVertical"]; - return ((vertical && [vertical intValue] > 0) ? (id)[vertical stringByAppendingFormat: @"px"] : nil); + return ((vertical && [vertical intValue] > 0) + ? (id)[vertical stringByAppendingFormat: @"px"] : nil); } - (NSString *) horizontalDragHandleStyle @@ -411,7 +350,8 @@ [self _setupContext]; horizontal = [moduleSettings objectForKey: @"DragHandleHorizontal"]; - return ((horizontal && [horizontal intValue] > 0) ? (id)[horizontal stringByAppendingFormat: @"px"] : nil); + return ((horizontal && [horizontal intValue] > 0) + ? (id)[horizontal stringByAppendingFormat: @"px"] : nil); } - (NSString *) contactsListContentStyle @@ -421,7 +361,8 @@ [self _setupContext]; height = [moduleSettings objectForKey: @"DragHandleVertical"]; - return ((height && [height intValue] > 0) ? [NSString stringWithFormat: @"%ipx", ([height intValue] - 27)] : nil); + return ((height && [height intValue] > 0) + ? [NSString stringWithFormat: @"%ipx", ([height intValue] - 27)] : nil); } - (WOResponse *) saveDragHandleStateAction diff --git a/UI/Contacts/UIxListView.m b/UI/Contacts/UIxListView.m index 36cacca6a..c4499dfd9 100644 --- a/UI/Contacts/UIxListView.m +++ b/UI/Contacts/UIxListView.m @@ -143,29 +143,23 @@ - (WOResponse *) propertiesAction { + NSArray *references; NSMutableArray *data; NGVCardReference *card; - WOResponse *rc; - int i, count; + int count, max; - data = [NSMutableArray array]; - co = [self clientObject]; - list = [co vList]; - - count = [[list cardReferences] count]; - for (i = 0; i < count; i++) + list = [[self clientObject] vList]; + references = [list cardReferences]; + max = [references count]; + data = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) { - card = [[list cardReferences] objectAtIndex: i]; - [data addObject: [NSArray arrayWithObjects: [card reference], [card fn], - [card email], nil]]; + card = [references objectAtIndex: count]; + [data addObject: [NSArray arrayWithObjects: [card reference], + [card fn], [card email], nil]]; } - rc = [context response]; - [rc setHeader: @"text/plain; charset=utf-8" - forKey: @"content-type"]; - [rc appendContentString: [data jsonRepresentation]]; - - return rc; + return [self responseWithStatus: 200 andJSONRepresentation: data]; } @end diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index c6a05e72c..945c9a136 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -23,11 +23,6 @@ pageName = "UIxContactFoldersView"; actionName = "mailerContacts"; }; - contactSearch = { - protectedBy = ""; - pageName = "UIxContactFoldersView"; - actionName = "contactSearch"; - }; allContactSearch = { protectedBy = ""; pageName = "UIxContactFoldersView"; diff --git a/UI/MailPartViewers/UIxMailPartICalActions.h b/UI/MailPartViewers/UIxMailPartICalActions.h index 4b254d62b..b90b921e0 100644 --- a/UI/MailPartViewers/UIxMailPartICalActions.h +++ b/UI/MailPartViewers/UIxMailPartICalActions.h @@ -33,6 +33,8 @@ - (WOResponse *) acceptAction; - (WOResponse *) declineAction; +- (WOResponse *) tentativeAction; +- (WOResponse *) delegateAction; - (WOResponse *) addToCalendarAction; - (WOResponse *) deleteFromCalendarAction; diff --git a/UI/MailPartViewers/UIxMailPartICalActions.m b/UI/MailPartViewers/UIxMailPartICalActions.m index c3a28625a..3805dc64c 100644 --- a/UI/MailPartViewers/UIxMailPartICalActions.m +++ b/UI/MailPartViewers/UIxMailPartICalActions.m @@ -48,7 +48,6 @@ #import #import #import -#import #import #import "UIxMailPartICalActions.h" @@ -261,6 +260,12 @@ withDelegate: nil]; } +- (WOResponse *) tentativeAction +{ + return [self _changePartStatusAction: @"TENTATIVE" + withDelegate: nil]; +} + - (WOResponse *) delegateAction { // BOOL receiveUpdates; @@ -369,7 +374,7 @@ address = [[mailObject fromEnvelopeAddresses] objectAtIndex: 0]; emailFrom = [address baseEMail]; - return [event findParticipantWithEmail: emailFrom]; + return [event findAttendeeWithEmail: emailFrom]; } - (BOOL) _updateParticipantStatusInEvent: (iCalEvent *) calendarEvent diff --git a/UI/MailPartViewers/UIxMailPartICalViewer.m b/UI/MailPartViewers/UIxMailPartICalViewer.m index b842246fc..80f8e62d6 100644 --- a/UI/MailPartViewers/UIxMailPartICalViewer.m +++ b/UI/MailPartViewers/UIxMailPartICalViewer.m @@ -44,7 +44,6 @@ #import #import #import -#import #import #import #import @@ -378,7 +377,7 @@ - (BOOL) isLoggedInUserAnAttendee { - return [[self authorativeEvent] userIsParticipant: [context activeUser]]; + return [[self authorativeEvent] userIsAttendee: [context activeUser]]; } - (NSString *) currentAttendeeClass @@ -451,7 +450,7 @@ { iCalPerson *currentUser; - currentUser = [[self authorativeEvent] findParticipant: [context activeUser]]; + currentUser = [[self authorativeEvent] userAsAttendee: [context activeUser]]; return currentUser; } @@ -463,7 +462,7 @@ should translate the email to an internal uid and then retrieve all emails addresses for matching the participant. - Note: -findParticipantWithEmail: does not parse the email! + Note: -findAttendeeWithEmail: does not parse the email! */ iCalEvent *e; iCalPerson *p; @@ -473,9 +472,9 @@ e = [self storedEvent]; if (e) { - p = [e findParticipantWithEmail: [self replySenderBaseEMail]]; + p = [e findAttendeeWithEmail: [self replySenderBaseEMail]]; if (!p) - p = [e findParticipantWithEmail:[self replySenderEMail]]; + p = [e findAttendeeWithEmail:[self replySenderEMail]]; } return p; @@ -496,7 +495,7 @@ address = [[mailObject fromEnvelopeAddresses] objectAtIndex: 0]; emailFrom = [address baseEMail]; - return [event findParticipantWithEmail: emailFrom]; + return [event findAttendeeWithEmail: emailFrom]; } - (BOOL) hasSenderStatusChanged diff --git a/UI/MailPartViewers/product.plist b/UI/MailPartViewers/product.plist index f5c59c51a..dd749362d 100644 --- a/UI/MailPartViewers/product.plist +++ b/UI/MailPartViewers/product.plist @@ -28,6 +28,11 @@ actionClass = "UIxMailPartICalActions"; actionName = "decline"; }; + tentative = { + protectedBy = "View"; + actionClass = "UIxMailPartICalActions"; + actionName = "tentative"; + }; delegate = { protectedBy = "View"; actionClass = "UIxMailPartICalActions"; diff --git a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings index d225c7b09..0a1611a13 100644 --- a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "A data que você informou ocorre antes da data ini = "Você tem certeza que quer apagar o calendário \"%{0}\"?"; /* Legend */ -"Required participant" = "Participante Requerido"; -"Optional participant" = "Participante Opcional"; +"Participant" = "Participante"; +"Optional Participant" = "Participante Opcional"; +"Non Participant" = "Não Participante"; "Chair" = "Cadeira"; "Needs action" = "Ações necessárias"; diff --git a/UI/Scheduler/Czech.lproj/Localizable.strings b/UI/Scheduler/Czech.lproj/Localizable.strings index d336dac7a..0db47391d 100644 --- a/UI/Scheduler/Czech.lproj/Localizable.strings +++ b/UI/Scheduler/Czech.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "Zadané datum konce je před začátkem události. = "Opravdu chcete smazat kalendář \"%{0}\"?"; /* Legend */ -"Required participant" = "Vyžadovaný účastník"; -"Optional participant" = "Nepovinný účastník"; +"Participant" = "Účastník"; +"Optional Participant" = "Nepovinný účastník"; +"Non Participant" = "Non Participant"; "Chair" = "Židle"; "Needs action" = "Vyžaduje akci"; diff --git a/UI/Scheduler/Dutch.lproj/Localizable.strings b/UI/Scheduler/Dutch.lproj/Localizable.strings index 8f8bc3940..c12a962d0 100644 --- a/UI/Scheduler/Dutch.lproj/Localizable.strings +++ b/UI/Scheduler/Dutch.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "Het begin vindt plaats vóór het einde."; = "Weet u zeker dat u de agenda \"%{0}\" wilt verwijderen?"; /* Legend */ -"Required participant" = "Vereiste deelnemer"; -"Optional participant" = "Gewenste deelnemer"; +"Participant" = "Deelnemer"; +"Optional Participant" = "Gewenste deelnemer"; +"Non Participant" = "Non Participant"; "Chair" = "Voorzitter"; "Needs action" = "Actie vereist"; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 9c27aab21..75a35cc04 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -246,10 +246,10 @@ /* Appointments (participation state) */ -"partStat_NEEDS-ACTION" = "Needs action"; +"partStat_NEEDS-ACTION" = "I will confirm later"; "partStat_ACCEPTED" = "I will attend"; "partStat_DECLINED" = "I will not attend"; -"partStat_TENTATIVE" = "I will confirm later"; +"partStat_TENTATIVE" = "I might attend"; "partStat_DELEGATED" = "I delegate"; "partStat_OTHER" = "Other"; @@ -460,8 +460,9 @@ validate_endbeforestart = "The end date that you entered occurs before the st = "Are you sure you want to delete the calendar \"%{0}\"?"; /* Legend */ -"Required participant" = "Required participant"; -"Optional participant" = "Optional participant"; +"Participant" = "Participant"; +"Optional Participant" = "Optional Participant"; +"Non Participant" = "Non Participant"; "Chair" = "Chair"; "Needs action" = "Needs action"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 9cdc2c7ec..1d44ae291 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -246,10 +246,10 @@ /* Appointments (participation state) */ -"partStat_NEEDS-ACTION" = "Décision attendue"; +"partStat_NEEDS-ACTION" = "Je confirmerai plus tard"; "partStat_ACCEPTED" = "Je participerai"; "partStat_DECLINED" = "Je ne participerai pas"; -"partStat_TENTATIVE" = "Je confirmerai plus tard"; +"partStat_TENTATIVE" = "Je participerai peut-être"; "partStat_DELEGATED" = "Je délègue"; "partStat_OTHER" = "???"; @@ -460,9 +460,10 @@ validate_endbeforestart = "La date de fin est avant la date de début."; = "Voulez-vous vraiment supprimer l'agenda «%{0}»?"; /* Legend */ -"Required participant" = "Participant obligatoire"; -"Optional participant" = "Participant facultatif"; -"Chair" = "Chaise"; +"Participant" = "Invité"; +"Optional Participant" = "Invité optionnel"; +"Non Participant" = "Non-invité"; +"Chair" = "Président"; "Needs action" = "En attente"; "Accepted" = "Accepté"; diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index ae4474590..f96487d1a 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "Ihr Beginn ist nach dem Ende"; = "Wollen Sie diesen Kalender wirklich löschen \"%{0}\"?"; /* Legend */ -"Required participant" = "notwendiger Teilnehmer"; -"Optional participant" = "optionaler Teilnehmer"; +"Participant" = "Teilnehmer"; +"Optional Participant" = "optionaler Teilnehmer"; +"Non Participant" = "Non Participant"; "Chair" = "Vorsitz"; "Needs action" = "Benötigt Eingriff"; diff --git a/UI/Scheduler/Hungarian.lproj/Localizable.strings b/UI/Scheduler/Hungarian.lproj/Localizable.strings index 0d9bc3897..09823cbef 100644 --- a/UI/Scheduler/Hungarian.lproj/Localizable.strings +++ b/UI/Scheduler/Hungarian.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "A megadott befejező dátum korábbi, mint a kezd = "Biztosan törli ezt a naptárat: \"%{0}\"?"; /* Legend */ -"Required participant" = "Kötelező résztvevő"; -"Optional participant" = "Nem kötelező résztvevő"; +"Participant" = "Kötelező résztvevő"; +"Optional Participant" = "Nem kötelező résztvevő"; +"Non Participant" = "Non Participant"; "Chair" = "Szék"; "Needs action" = "Foglalkozni kell vele"; diff --git a/UI/Scheduler/Italian.lproj/Localizable.strings b/UI/Scheduler/Italian.lproj/Localizable.strings index b965bf248..6b5830275 100644 --- a/UI/Scheduler/Italian.lproj/Localizable.strings +++ b/UI/Scheduler/Italian.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "La data finale specificata è precedente alla data = "Sei sicuro di voler cancellare il calendario \"%{0}\"?"; /* Legend */ -"Required participant" = "Richiede partecipanti"; -"Optional participant" = "Partecipanti opzionali"; +"Participant" = "Partecipanti"; +"Optional Participant" = "Partecipanti opzionali"; +"Non Participant" = "Non Partecipanti"; "Chair" = "Sedia"; "Needs action" = "Richiede un'azione"; diff --git a/UI/Scheduler/Russian.lproj/Localizable.strings b/UI/Scheduler/Russian.lproj/Localizable.strings index f90977000..370af8a7c 100644 --- a/UI/Scheduler/Russian.lproj/Localizable.strings +++ b/UI/Scheduler/Russian.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "The end date that you entered occurs before the st = "Вы уверены что хотите удалить календарь \"%{0}\"?"; /* Legend */ -"Required participant" = "Required participant"; -"Optional participant" = "Optional participant"; +"Participant" = "Participant"; +"Optional Participant" = "Optional Participant"; +"Non Participant" = "Non Participant"; "Chair" = "Chair"; "Needs action" = "Needs action"; diff --git a/UI/Scheduler/Spanish.lproj/Localizable.strings b/UI/Scheduler/Spanish.lproj/Localizable.strings index 38245e2e0..a83166f5d 100644 --- a/UI/Scheduler/Spanish.lproj/Localizable.strings +++ b/UI/Scheduler/Spanish.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "Su fecha/hora de comienzo es posterio a la de fina = "¿Está seguro/a que desea borrar el calendario \"%{0}\"?"; /* Legend */ -"Required participant" = "Asistente obligatorio"; -"Optional participant" = "Asistentes opcionales"; +"Participant" = "Asistente"; +"Optional Participant" = "Asistentes opcionales"; +"Non Participant" = "Non Particpant"; "Chair" = "Presidente"; "Needs action" = "Requiere intervención"; diff --git a/UI/Scheduler/Swedish.lproj/Localizable.strings b/UI/Scheduler/Swedish.lproj/Localizable.strings index 978b39809..4258a297d 100644 --- a/UI/Scheduler/Swedish.lproj/Localizable.strings +++ b/UI/Scheduler/Swedish.lproj/Localizable.strings @@ -460,8 +460,9 @@ validate_endbeforestart = "Angivet slutdatumet inträffar före angivet start = "Är du säker på att du vill ta bort kalendern \"%{0}\"?"; /* Legend */ -"Required participant" = "Deltagande krävs"; -"Optional participant" = "Deltagande valfritt"; +"Participant" = "Deltagande krävs"; +"Optional Participant" = "Deltagande valfritt"; +"Non Participant" = "Non Participant"; "Chair" = "Stol"; "Needs action" = "Behöver åtgärd"; diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 8873dd55c..e13d76644 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -522,22 +522,32 @@ [event setTransparency: (isTransparent? @"TRANSPARENT" : @"OPAQUE")]; } -// TODO: add tentatively +- (id) _statusChangeAction: (NSString *) newStatus +{ + [[self clientObject] changeParticipationStatus: newStatus + withDelegate: nil]; + + return [self responseWith204]; +} - (id) acceptAction { - [[self clientObject] changeParticipationStatus: @"ACCEPTED" - withDelegate: nil]; - - return self; + return [self _statusChangeAction: @"ACCEPTED"]; } - (id) declineAction { - [[self clientObject] changeParticipationStatus: @"DECLINED" - withDelegate: nil]; + return [self _statusChangeAction: @"DECLINED"]; +} - return self; +- (id) needsActionAction +{ + return [self _statusChangeAction: @"NEEDS-ACTION"]; +} + +- (id) tentativeAction +{ + return [self _statusChangeAction: @"TENTATIVE"]; } - (id) delegateAction @@ -584,7 +594,7 @@ reason: @"missing 'to' parameter"]; if (!response) - response = [self responseWithStatus: 200]; + response = [self responseWith204]; return response; } diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 63bee5845..25348ead5 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -54,7 +54,6 @@ #import #import #import -#import #import #import #import @@ -268,6 +267,8 @@ iRANGE(2); [currentAttendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"partstat"]; + [currentAttendeeData setObject: [[currentAttendee role] lowercaseString] + forKey: @"role"]; if ([[currentAttendee delegatedTo] length]) [currentAttendeeData setObject: [[currentAttendee delegatedTo] rfc822Email] @@ -587,7 +588,7 @@ iRANGE(2); um = [SOGoUserManager sharedUserManager]; owner = [componentCalendar ownerInContext: context]; ownerEmail = [um getEmailForUID: owner]; - ASSIGN (ownerAsAttendee, [component findParticipantWithEmail: (id)ownerEmail]); + ASSIGN (ownerAsAttendee, [component findAttendeeWithEmail: (id)ownerEmail]); } } // /* cycles */ @@ -990,14 +991,26 @@ iRANGE(2); { NSString *word; - if ([item intValue] == iCalPersonPartStatAccepted) - word = @"ACCEPTED"; - else if ([item intValue] == iCalPersonPartStatDeclined) - word = @"DECLINED"; - else if ([item intValue] == iCalPersonPartStatDelegated) - word = @"DELEGATED"; - else - word = @"UNKNOWN"; + switch ([item intValue]) + { + case iCalPersonPartStatAccepted: + word = @"ACCEPTED"; + break; + case iCalPersonPartStatDeclined: + word = @"DECLINED"; + break; + case iCalPersonPartStatNeedsAction: + word = @"NEEDS-ACTION"; + break; + case iCalPersonPartStatTentative: + word = @"TENTATIVE"; + break; + case iCalPersonPartStatDelegated: + word = @"DELEGATED"; + break; + default: + word = @"UNKNOWN"; + } return [self labelForKey: [NSString stringWithFormat: @"partStat_%@", word]]; } @@ -1005,8 +1018,10 @@ iRANGE(2); - (NSArray *) replyList { return [NSArray arrayWithObjects: - [NSNumber numberWithInt: iCalPersonPartStatAccepted], + [NSNumber numberWithInt: iCalPersonPartStatAccepted], [NSNumber numberWithInt: iCalPersonPartStatDeclined], + [NSNumber numberWithInt: iCalPersonPartStatNeedsAction], + [NSNumber numberWithInt: iCalPersonPartStatTentative], [NSNumber numberWithInt: iCalPersonPartStatDelegated], nil]; } @@ -1508,7 +1523,7 @@ RANGE(2); unsigned int count, max; NSString *currentEmail; iCalPerson *currentAttendee; - NSString *json; + NSString *json, *role, *partstat; NSDictionary *attendeesData; NSArray *attendees; NSDictionary *currentData; @@ -1529,17 +1544,33 @@ RANGE(2); { currentData = [attendees objectAtIndex: count]; currentEmail = [currentData objectForKey: @"email"]; - currentAttendee = [component findParticipantWithEmail: currentEmail]; + role = [[currentData objectForKey: @"role"] uppercaseString]; + if (!role) + role = @"REQ-PARTICIPANT"; + if ([role isEqualToString: @"NON-PARTICIPANT"]) + partstat = @""; + else + { + partstat = [[currentData objectForKey: @"partstat"] + uppercaseString]; + if (!partstat) + partstat = @"NEEDS-ACTION"; + } + currentAttendee = [component findAttendeeWithEmail: currentEmail]; if (!currentAttendee) { currentAttendee = [iCalPerson elementWithTag: @"attendee"]; [currentAttendee setCn: [currentData objectForKey: @"name"]]; [currentAttendee setEmail: currentEmail]; - [currentAttendee setRole: @"REQ-PARTICIPANT"]; - [currentAttendee setRsvp: @"TRUE"]; - [currentAttendee - setParticipationStatus: iCalPersonPartStatNeedsAction]; + // [currentAttendee + // setParticipationStatus: iCalPersonPartStatNeedsAction]; } + [currentAttendee + setRsvp: ([role isEqualToString: @"NON-PARTICIPANT"] + ? @"FALSE" + : @"TRUE")]; + [currentAttendee setRole: role]; + [currentAttendee setPartStat: partstat]; [newAttendees addObject: currentAttendee]; } [component setAttendees: newAttendees]; @@ -1965,8 +1996,7 @@ RANGE(2); isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]] - || ([[component attendees] count] - && [component userIsParticipant: ownerUser] + || ([component userIsAttendee: ownerUser] && !isOrganizer // Lightning does not manage participation status within tasks, // so we also ignore the participation status of tasks in the @@ -1991,14 +2021,12 @@ RANGE(2); { SoSecurityManager *sm; NSString *toolbarFilename, *adminToolbar; - SOGoUser *currentUser; if ([clientObject isKindOfClass: [SOGoAppointmentObject class]]) adminToolbar = @"SOGoAppointmentObject.toolbar"; else adminToolbar = @"SOGoTaskObject.toolbar"; - currentUser = [context activeUser]; sm = [SoSecurityManager sharedSecurityManager]; if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent @@ -2035,36 +2063,45 @@ RANGE(2); - (int) ownerIsAttendee: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject + andClientObject: (SOGoContentObject + *) clientObject { BOOL isOrganizer; - int rc = 0; + iCalPerson *ownerAttendee; + int rc; + + rc = 0; isOrganizer = [component userIsOrganizer: ownerUser]; if (isOrganizer) isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; - if ([[component attendees] count] - && [component userIsParticipant: ownerUser] - && !isOrganizer - && ![[component tag] isEqualToString: @"VTODO"]) - rc = 1; + if (!isOrganizer && ![[component tag] isEqualToString: @"VTODO"]) + { + ownerAttendee = [component userAsAttendee: ownerUser]; + if (ownerAttendee) + { + if ([[ownerAttendee rsvp] isEqualToString: @"true"]) + rc = 1; + else + rc = 2; + } + } return rc; } - (int) delegateIsAttendee: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject + andClientObject: (SOGoContentObject + *) clientObject { SoSecurityManager *sm; - SOGoUser *currentUser; - int rc = 0; + iCalPerson *ownerAttendee; + int rc; + + rc = 0; - currentUser = [context activeUser]; sm = [SoSecurityManager sharedSecurityManager]; - if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent onObject: clientObject inContext: context]) @@ -2072,11 +2109,15 @@ RANGE(2); andClientObject: clientObject]; else if (![sm validatePermission: SOGoCalendarPerm_RespondToComponent onObject: clientObject - inContext: context] - && [[component attendees] count] - && [component userIsParticipant: ownerUser] - && ![component userIsOrganizer: ownerUser]) - rc = 1; + inContext: context]) + { + ownerAttendee = [component userAsAttendee: ownerUser]; + if ([[ownerAttendee rsvp] isEqualToString: @"true"] + && ![component userIsOrganizer: ownerUser]) + rc = 1; + else + rc = 2; + } else rc = 2; // not invited, just RO @@ -2090,9 +2131,8 @@ RANGE(2); int rc; clientObject = [self clientObject]; - ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context] - roles: nil]; - + ownerUser + = [SOGoUser userWithLogin: [clientObject ownerInContext: context]]; if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]]) rc = 2; else @@ -2113,9 +2153,54 @@ RANGE(2); return [self getEventRWType] != 0; } -- (BOOL) userIsAttendee +- (BOOL) userHasRSVP { - return [self getEventRWType] == 1; + return ([self getEventRWType] == 1); +} + +- (NSString *) currentAttendeeClasses +{ + NSMutableArray *classes; + iCalPerson *ownerAttendee; + SOGoUser *ownerUser; + NSString *role, *partStat; + SOGoCalendarComponent *co; + + classes = [NSMutableArray arrayWithCapacity: 5]; + + /* rsvp class */ + if (![[attendee rsvp] isEqualToString: @"true"]) + [classes addObject: @"not-rsvp"]; + + /* partstat class */ + partStat = [[attendee partStat] lowercaseString]; + if (![partStat length]) + partStat = @"no-partstat"; + [classes addObject: partStat]; + + /* role class */ + role = [[attendee role] lowercaseString]; + if (![partStat length]) + role = @"no-role"; + [classes addObject: role]; + + /* attendee class */ + if ([[attendee delegatedFrom] length] > 0) + [classes addObject: @"delegate"]; + + /* current attendee class */ + co = [self clientObject]; + ownerUser = [SOGoUser userWithLogin: [co ownerInContext: context]]; + ownerAttendee = [component userAsAttendee: ownerUser]; + if (attendee == ownerAttendee) + [classes addObject: @"attendeeUser"]; + + return [classes componentsJoinedByString: @" "]; +} + +- (NSString *) ownerLogin +{ + return [[self clientObject] ownerInContext: context]; } @end diff --git a/UI/Scheduler/Welsh.lproj/Localizable.strings b/UI/Scheduler/Welsh.lproj/Localizable.strings index ddfed3d86..d91595117 100644 --- a/UI/Scheduler/Welsh.lproj/Localizable.strings +++ b/UI/Scheduler/Welsh.lproj/Localizable.strings @@ -462,6 +462,7 @@ validate_endbeforestart = "Mae'r dyddiad gorffen sydd wedi'i roi yn digwydd c /* Legend */ "Required participant" = "Cyfranogwr gofynnol"; "Optional participant" = "Cyfranogwr opsiynol"; +"Non Participant" = "Non Participant"; "Chair" = "Cadair"; "Needs action" = "Angen gweithred"; diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index e3a88736e..63238ad16 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -235,6 +235,16 @@ pageName = "UIxAppointmentEditor"; actionName = "delegate"; }; + tentative = { + protectedBy = "RespondToComponent"; + pageName = "UIxAppointmentEditor"; + actionName = "tentative"; + }; + needsaction = { + protectedBy = "RespondToComponent"; + pageName = "UIxAppointmentEditor"; + actionName = "needsAction"; + }; adjust = { protectedBy = "ModifyComponent"; actionClass = "UIxAppointmentActions"; diff --git a/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox b/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox index ec747f7ab..b0e61dc8e 100644 --- a/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox +++ b/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox @@ -45,6 +45,7 @@

+ @@ -56,6 +57,11 @@ + + + + @@ -76,6 +82,7 @@ + @@ -223,7 +230,9 @@ : - +

( , )
diff --git a/UI/Templates/SchedulerUI/UIxAttendeesEditor.wox b/UI/Templates/SchedulerUI/UIxAttendeesEditor.wox index 90a02d789..f3f35da26 100644 --- a/UI/Templates/SchedulerUI/UIxAttendeesEditor.wox +++ b/UI/Templates/SchedulerUI/UIxAttendeesEditor.wox @@ -22,29 +22,23 @@
- + > + + checked="1" id="onlyOfficeHours" />
- +
+ + +
- - - -
+
@@ -52,25 +46,28 @@
- - - - -
+ +
+
+ +
- - - - -
+ + + + + @@ -78,27 +75,38 @@
-
    +
      +
    • +
    • +
    • +
    • +
    + +
    • - - + +
-

+ > + >
diff --git a/UI/Templates/SchedulerUI/UIxComponentEditor.wox b/UI/Templates/SchedulerUI/UIxComponentEditor.wox index 8fd1238b5..bb13b41a2 100644 --- a/UI/Templates/SchedulerUI/UIxComponentEditor.wox +++ b/UI/Templates/SchedulerUI/UIxComponentEditor.wox @@ -19,7 +19,8 @@ var activeComponent = '/'; var readOnly = truefalse; - var attendees = ; + var attendees = ; + var ownerLogin = ''; @@ -203,11 +204,9 @@ - +