From 2bf4b7cf8b0e369a2af84f2377188a5c82172f19 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Wed, 5 May 2010 14:40:53 +0000 Subject: [PATCH] Monotone-Parent: 1ccd34fd9ea99e675935d745db7e70d19716428a Monotone-Revision: c40151185171e3ba8a257569b8ab5ec86930d393 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-05-05T14:40:53 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 23 +++ UI/MailPartViewers/UIxMailPartICalActions.h | 2 + UI/MailPartViewers/UIxMailPartICalActions.m | 9 +- UI/MailPartViewers/product.plist | 5 + .../Localizable.strings | 5 +- UI/Scheduler/Czech.lproj/Localizable.strings | 5 +- UI/Scheduler/Dutch.lproj/Localizable.strings | 5 +- .../English.lproj/Localizable.strings | 9 +- UI/Scheduler/French.lproj/Localizable.strings | 11 +- UI/Scheduler/German.lproj/Localizable.strings | 5 +- .../Hungarian.lproj/Localizable.strings | 5 +- .../Italian.lproj/Localizable.strings | 5 +- .../Russian.lproj/Localizable.strings | 5 +- .../Spanish.lproj/Localizable.strings | 5 +- .../Swedish.lproj/Localizable.strings | 5 +- UI/Scheduler/UIxAppointmentEditor.m | 28 ++- UI/Scheduler/UIxComponentEditor.m | 175 ++++++++++++----- UI/Scheduler/Welsh.lproj/Localizable.strings | 1 + UI/Scheduler/product.plist | 10 + .../MailPartViewers/UIxMailPartICalViewer.wox | 11 +- .../SchedulerUI/UIxAttendeesEditor.wox | 86 +++++---- .../SchedulerUI/UIxComponentEditor.wox | 22 ++- UI/WebServerResources/MailerUI.css | 34 ++-- UI/WebServerResources/SchedulerUI.css | 15 +- UI/WebServerResources/SchedulerUI.js | 4 +- .../UIxAppointmentEditor.css | 69 ++++--- UI/WebServerResources/UIxAppointmentEditor.js | 36 ++-- UI/WebServerResources/UIxAttendeesEditor.css | 103 ++++++---- UI/WebServerResources/UIxAttendeesEditor.js | 176 +++++++++++++----- UI/WebServerResources/UIxComponentEditor.js | 12 +- UI/WebServerResources/accepted.png | Bin 510 -> 0 bytes UI/WebServerResources/accepted.selected.png | Bin 511 -> 0 bytes UI/WebServerResources/attendee-partstats.png | Bin 0 -> 2091 bytes UI/WebServerResources/attendee-roles.png | Bin 0 -> 1933 bytes UI/WebServerResources/declined.png | Bin 419 -> 0 bytes UI/WebServerResources/declined.selected.png | Bin 354 -> 0 bytes UI/WebServerResources/delegated.png | Bin 540 -> 0 bytes UI/WebServerResources/delegated.selected.png | Bin 452 -> 0 bytes UI/WebServerResources/generic.js | 2 +- UI/WebServerResources/iefixes.css | 16 ++ UI/WebServerResources/needs-action.png | Bin 506 -> 0 bytes .../needs-action.selected.png | Bin 428 -> 0 bytes 42 files changed, 625 insertions(+), 279 deletions(-) delete mode 100644 UI/WebServerResources/accepted.png delete mode 100644 UI/WebServerResources/accepted.selected.png create mode 100644 UI/WebServerResources/attendee-partstats.png create mode 100644 UI/WebServerResources/attendee-roles.png delete mode 100644 UI/WebServerResources/declined.png delete mode 100644 UI/WebServerResources/declined.selected.png delete mode 100644 UI/WebServerResources/delegated.png delete mode 100644 UI/WebServerResources/delegated.selected.png delete mode 100644 UI/WebServerResources/needs-action.png delete mode 100644 UI/WebServerResources/needs-action.selected.png diff --git a/ChangeLog b/ChangeLog index ec184ffb3..2c2b2c9a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,28 @@ 2010-05-05 Wolfgang Sourdeau + * 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 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/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 @@ - +