mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-02 02:02:32 +00:00
(feat) Handle invitations in appointment viewer
This commit is contained in:
@@ -200,7 +200,7 @@
|
||||
"General" = "General";
|
||||
"Reply" = "Reply";
|
||||
"Created by" = "Created by";
|
||||
|
||||
"You are invited to participate" = "You are invited to participate";
|
||||
|
||||
"Target:" = "Target:";
|
||||
|
||||
@@ -431,6 +431,7 @@ validate_endbeforestart = "The end date that you entered occurs before the st
|
||||
"New Event" = "New Event";
|
||||
"New Task" = "New Task";
|
||||
"Edit" = "Edit";
|
||||
"Update" = "Update";
|
||||
"Delete" = "Delete";
|
||||
"Go to Today" = "Go to Today";
|
||||
"Day View" = "Day View";
|
||||
@@ -523,6 +524,8 @@ vtodo_class2 = "(Confidential task)";
|
||||
"button_allOccurrences" = "All occurences";
|
||||
"Edit This Occurrence" = "Edit This Occurrence";
|
||||
"Edit All Occurrences" = "Edit All Occurrences";
|
||||
"Update This Occurrence" = "Update This Occurrence";
|
||||
"Update All Occurrences" = "Update All Occurrences";
|
||||
|
||||
/* Properties dialog */
|
||||
"Name" = "Name";
|
||||
|
||||
@@ -237,55 +237,77 @@
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
/**
|
||||
* @api {post} /so/:username/Calendar/:calendarId/:appointmentId/rsvpAppointment Set participation state
|
||||
* @apiVersion 1.0.0
|
||||
* @apiName PostEventRsvp
|
||||
* @apiGroup Calendar
|
||||
* @apiDescription Set the participation state of an attendee.
|
||||
* @apiExample {curl} Example usage:
|
||||
* curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/71B6-54904400-1-7C308500.ics/rsvpAppointment \
|
||||
* -H 'Content-Type: application/json' \
|
||||
* -d '{ "reply": 1, \
|
||||
* "alarm": { { "quantity": 15, "unit": "MINUTES", "action": "display", "reference": "BEFORE", "relation": "START" } }'
|
||||
*
|
||||
* @apiParam {Number} reply 0 if needs-action, 1 if accepted, 2 if declined, 3 if tentative, 4 if delegated
|
||||
* @apiParam {String} [delegatedTo] Email address of delegated attendee
|
||||
* @apiParam {Object[]} [alarm] Set an alarm for the attendee
|
||||
* @apiParam {String} alarm.action Either display or email
|
||||
* @apiParam {Number} alarm.quantity Quantity of units
|
||||
* @apiParam {String} alarm.unit Either MINUTES, HOURS, or DAYS
|
||||
* @apiParam {String} alarm.reference Either BEFORE or AFTER
|
||||
* @apiParam {String} alarm.relation Either START or END
|
||||
* @apiParam {Boolean} [alarm.attendees] Alert attendees by email if 1 and action is email
|
||||
* @apiParam {Boolean} [alarm.organizer] Alert organizer by email if 1 and action is email
|
||||
*/
|
||||
- (id <WOActionResults>) rsvpAction
|
||||
{
|
||||
iCalPerson *delegatedAttendee;
|
||||
NSDictionary *message;
|
||||
NSDictionary *params, *jsonResponse;
|
||||
WOResponse *response;
|
||||
WORequest *request;
|
||||
iCalAlarm *anAlarm;
|
||||
NSException *ex;
|
||||
NSString *status;
|
||||
id alarm;
|
||||
|
||||
int replyList, reminderList;
|
||||
int replyList;
|
||||
|
||||
request = [context request];
|
||||
message = [[request contentAsString] objectFromJSONString];
|
||||
params = [[request contentAsString] objectFromJSONString];
|
||||
|
||||
delegatedAttendee = nil;
|
||||
anAlarm = nil;
|
||||
status = nil;
|
||||
|
||||
replyList = [[message objectForKey: @"replyList"] intValue];
|
||||
replyList = [[params objectForKey: @"reply"] intValue];
|
||||
|
||||
switch (replyList)
|
||||
{
|
||||
case 0:
|
||||
case iCalPersonPartStatAccepted:
|
||||
status = @"ACCEPTED";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case iCalPersonPartStatDeclined:
|
||||
status = @"DECLINED";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case iCalPersonPartStatNeedsAction:
|
||||
status = @"NEEDS-ACTION";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case iCalPersonPartStatTentative:
|
||||
status = @"TENTATIVE";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case iCalPersonPartStatDelegated:
|
||||
default:
|
||||
{
|
||||
NSString *delegatedEmail, *delegatedUid;
|
||||
SOGoUser *user;
|
||||
|
||||
status = @"DELEGATED";
|
||||
delegatedEmail = [[message objectForKey: @"delegatedTo"] stringByTrimmingSpaces];
|
||||
delegatedEmail = [[params objectForKey: @"delegatedTo"] stringByTrimmingSpaces];
|
||||
|
||||
if ([delegatedEmail length])
|
||||
{
|
||||
@@ -308,60 +330,58 @@
|
||||
[NSString stringWithFormat: @"mailto:%@", [[user allEmails] objectAtIndex: 0]]];
|
||||
}
|
||||
else
|
||||
return [NSException exceptionWithHTTPStatus: 400
|
||||
reason: @"missing 'to' parameter"];
|
||||
{
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"failure", @"status",
|
||||
@"missing 'delegatedTo' parameter", @"message",
|
||||
nil];
|
||||
return [self responseWithStatus: 400
|
||||
andString: [jsonResponse jsonRepresentation]];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract the user alarm, if any
|
||||
reminderList = [[message objectForKey: @"reminderList"] intValue];
|
||||
// Set an alarm for the user
|
||||
alarm = [params objectForKey: @"alarm"];
|
||||
if ([alarm isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
NSString *reminderAction, *reminderUnit, *reminderQuantity, *reminderReference, *reminderRelation;
|
||||
BOOL reminderEmailAttendees, reminderEmailOrganizer;
|
||||
|
||||
if ([[message objectForKey: @"reminderList"] isEqualToString: @"WONoSelectionString"] || reminderList == 5 || reminderList == 10 || reminderList == 14)
|
||||
{
|
||||
// No selection, wipe alarm which will be done in changeParticipationStatus...
|
||||
}
|
||||
else if (reminderList == 15)
|
||||
{
|
||||
// Custom
|
||||
reminderAction = [alarm objectForKey: @"action"];
|
||||
reminderUnit = [alarm objectForKey: @"unit"];
|
||||
reminderQuantity = [alarm objectForKey: @"quantity"];
|
||||
reminderReference = [alarm objectForKey: @"reference"];
|
||||
reminderRelation = [alarm objectForKey: @"relation"];
|
||||
reminderEmailAttendees = [[alarm objectForKey: @"attendees"] boolValue];
|
||||
reminderEmailOrganizer = [[alarm objectForKey: @"organizer"] boolValue];
|
||||
anAlarm = [iCalAlarm alarmForEvent: [self event]
|
||||
owner: [[self clientObject] ownerInContext: context]
|
||||
action: [message objectForKey: @"reminderAction"]
|
||||
unit: [message objectForKey: @"reminderUnit"]
|
||||
quantity: [message objectForKey: @"reminderQuantity"]
|
||||
reference: [message objectForKey: @"reminderReference"]
|
||||
reminderRelation: [message objectForKey: @"reminderRelation"]
|
||||
emailAttendees: [[message objectForKey: @"reminderEmailAttendees"] boolValue]
|
||||
emailOrganizer: [[message objectForKey: @"reminderEmailOrganizer"] boolValue]];
|
||||
action: reminderAction
|
||||
unit: reminderUnit
|
||||
quantity: reminderQuantity
|
||||
reference: reminderReference
|
||||
reminderRelation: reminderRelation
|
||||
emailAttendees: reminderEmailAttendees
|
||||
emailOrganizer: reminderEmailOrganizer];
|
||||
}
|
||||
|
||||
ex = [[self clientObject] changeParticipationStatus: status
|
||||
withDelegate: delegatedAttendee
|
||||
alarm: anAlarm];
|
||||
|
||||
if (ex)
|
||||
{
|
||||
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[ex reason], @"message",
|
||||
nil];
|
||||
response = [self responseWithStatus: [ex httpStatus]
|
||||
andString: [jsonResponse jsonRepresentation]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard
|
||||
NSString *aValue;
|
||||
|
||||
aValue = [[UIxComponentEditor reminderValues] objectAtIndex: reminderList];
|
||||
|
||||
// Predefined alarm
|
||||
if ([aValue length])
|
||||
{
|
||||
iCalTrigger *aTrigger;
|
||||
|
||||
anAlarm = [[[iCalAlarm alloc] init] autorelease];
|
||||
aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"];
|
||||
[aTrigger setValueType: @"DURATION"];
|
||||
[anAlarm setTrigger: aTrigger];
|
||||
[anAlarm setAction: @"DISPLAY"];
|
||||
[aTrigger setSingleValue: aValue forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
response = (WOResponse *)[[self clientObject] changeParticipationStatus: status
|
||||
withDelegate: delegatedAttendee
|
||||
alarm: anAlarm];
|
||||
|
||||
if (!response)
|
||||
response = [self responseWith204];
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -413,9 +433,7 @@
|
||||
* @apiParam {String} alarm.reference Either BEFORE or AFTER
|
||||
* @apiParam {String} alarm.relation Either START or END
|
||||
* @apiParam {Boolean} [alarm.attendees] Alert attendees by email if true and action is email
|
||||
* @apiParam {Object} [alarm.organizer] Alert organizer at this email address if action is email
|
||||
* @apiParam {String} [alarm.organizer.name] Attendee's name
|
||||
* @apiParam {String} alarm.organizer.email Attendee's email address
|
||||
* @apiParam {Boolean} [alarm.organizer] Alert organizer by email if true and action is email
|
||||
*
|
||||
* @apiParam {_} ... _Save in [iCalRepeatbleEntityObject+SOGo setAttributes:inContext:]_
|
||||
*
|
||||
@@ -561,6 +579,8 @@
|
||||
* @apiSuccess (Success 200) {String} localizedEndDate Formatted end date
|
||||
* @apiSuccess (Success 200) {String} [localizedEndTime] Formatted end time
|
||||
* @apiSuccess (Success 200) {Number} isReadOnly 1 if event is read-only
|
||||
* @apiSuccess (Success 200) {Number} userHasRSVP 1 if owner is invited
|
||||
* @apiSuccess (Success 200) {Number} [reply] 0 if needs-action, 1 if accepted, 2 if declined, 3 if tentative, 4 if delegated
|
||||
* @apiSuccess (Success 200) {Object[]} [attachUrls] Attached URLs
|
||||
* @apiSuccess (Success 200) {String} attachUrls.value URL
|
||||
*
|
||||
@@ -572,10 +592,8 @@
|
||||
* @apiSuccess (Success 200) {String} alarm.unit Either MINUTES, HOURS, or DAYS
|
||||
* @apiSuccess (Success 200) {String} alarm.reference Either BEFORE or AFTER
|
||||
* @apiSuccess (Success 200) {String} alarm.relation Either START or END
|
||||
* @apiSuccess (Success 200) {Object[]} [alarm.attendees] List of attendees
|
||||
* @apiSuccess (Success 200) {String} [alarm.attendees.name] Attendee's name
|
||||
* @apiSuccess (Success 200) {String} alarm.attendees.email Attendee's email address
|
||||
* @apiSuccess (Success 200) {String} [alarm.attendees.uid] System user ID
|
||||
* @apiSuccess (Success 200) {Boolean} alarm.attendees Alert attendees by email if true and action is email
|
||||
* @apiSuccess (Success 200) {Boolean} alarm.organizer Alert organizer by email if true and action is email
|
||||
*
|
||||
* @apiSuccess {_} ... _From [iCalEvent+SOGo attributes]_
|
||||
*
|
||||
@@ -687,6 +705,7 @@
|
||||
[componentCalendar nameInContainer], @"pid",
|
||||
[componentCalendar displayName], @"calendar",
|
||||
[NSNumber numberWithBool: [self isReadOnly]], @"isReadOnly",
|
||||
[NSNumber numberWithBool: [self userHasRSVP]], @"userHasRSVP",
|
||||
[dateFormatter formattedDate: eventStartDate], @"localizedStartDate",
|
||||
[dateFormatter formattedDate: eventEndDate], @"localizedEndDate",
|
||||
[self alarm], @"alarm",
|
||||
@@ -711,6 +730,9 @@
|
||||
[data setObject: [dateFormatter formattedTime: eventEndDate] forKey: @"localizedEndTime"];
|
||||
}
|
||||
|
||||
if ([self userHasRSVP])
|
||||
[data setObject: [self reply] forKey: @"reply"];
|
||||
|
||||
// Add attributes from iCalEvent+SOGo, iCalEntityObject+SOGo and iCalRepeatableEntityObject+SOGo
|
||||
[data addEntriesFromDictionary: [event attributesInContext: context]];
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
@@ -329,9 +331,67 @@
|
||||
|
||||
@end
|
||||
|
||||
/* Component Viewer, parent class of Appointment Viewer and Task Viewer */
|
||||
|
||||
@interface UIxComponentViewTemplate : UIxComponent
|
||||
{
|
||||
id item;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation UIxComponentViewTemplate
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
item = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[item release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setItem: (id) _item
|
||||
{
|
||||
ASSIGN (item, _item);
|
||||
}
|
||||
|
||||
- (id) item
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
- (NSArray *) replyList
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
[NSNumber numberWithInt: iCalPersonPartStatAccepted],
|
||||
[NSNumber numberWithInt: iCalPersonPartStatDeclined],
|
||||
[NSNumber numberWithInt: iCalPersonPartStatNeedsAction],
|
||||
[NSNumber numberWithInt: iCalPersonPartStatTentative],
|
||||
[NSNumber numberWithInt: iCalPersonPartStatDelegated],
|
||||
nil];
|
||||
}
|
||||
|
||||
- (NSString *) itemReplyText
|
||||
{
|
||||
NSString *word;
|
||||
|
||||
word = [iCalPerson descriptionForParticipationStatus: [item intValue]];
|
||||
|
||||
return [self labelForKey: [NSString stringWithFormat: @"partStat_%@", word]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* Appointment Viewer */
|
||||
|
||||
@interface UIxAppointmentViewTemplate : UIxComponent
|
||||
@interface UIxAppointmentViewTemplate : UIxComponentViewTemplate
|
||||
@end
|
||||
|
||||
@implementation UIxAppointmentViewTemplate
|
||||
@@ -339,7 +399,7 @@
|
||||
|
||||
/* Task Viewer */
|
||||
|
||||
@interface UIxTaskViewTemplate : UIxComponent
|
||||
@interface UIxTaskViewTemplate : UIxComponentViewTemplate
|
||||
@end
|
||||
|
||||
@implementation UIxTaskViewTemplate
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
}
|
||||
|
||||
- (BOOL) isReadOnly;
|
||||
- (BOOL) userHasRSVP;
|
||||
- (NSNumber *) reply;
|
||||
- (BOOL) isChildOccurrence;
|
||||
- (void) setAttributes: (NSDictionary *) attributes;
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
#import "UIxComponentEditor.h"
|
||||
#import "UIxDatePicker.h"
|
||||
|
||||
#define componentReadableWritable 0
|
||||
#define componentOwnerIsInvited 1
|
||||
#define componentReadableOnly 2
|
||||
|
||||
static NSArray *reminderItems = nil;
|
||||
static NSArray *reminderValues = nil;
|
||||
|
||||
@@ -317,14 +321,21 @@ static NSArray *reminderValues = nil;
|
||||
// nil];
|
||||
//}
|
||||
//
|
||||
//- (NSNumber *) reply
|
||||
//{
|
||||
// iCalPersonPartStat participationStatus;
|
||||
//
|
||||
// participationStatus = [ownerAsAttendee participationStatus];
|
||||
//
|
||||
// return [NSNumber numberWithInt: participationStatus];
|
||||
//}
|
||||
- (NSNumber *) reply
|
||||
{
|
||||
NSString *owner, *ownerEmail;
|
||||
SOGoUserManager *um;
|
||||
iCalPerson *ownerAsAttendee;
|
||||
iCalPersonPartStat participationStatus;
|
||||
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
owner = [componentCalendar ownerInContext: context];
|
||||
ownerEmail = [um getEmailForUID: owner];
|
||||
ownerAsAttendee = [component findAttendeeWithEmail: (id)ownerEmail];
|
||||
participationStatus = [ownerAsAttendee participationStatus];
|
||||
|
||||
return [NSNumber numberWithInt: participationStatus];
|
||||
}
|
||||
|
||||
///* priorities */
|
||||
//
|
||||
@@ -798,7 +809,7 @@ static NSArray *reminderValues = nil;
|
||||
iCalPerson *ownerAttendee;
|
||||
int rc;
|
||||
|
||||
rc = 0;
|
||||
rc = componentReadableWritable;
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent
|
||||
@@ -813,42 +824,42 @@ static NSArray *reminderValues = nil;
|
||||
ownerAttendee = [component userAsAttendee: ownerUser];
|
||||
if ([[ownerAttendee rsvp] isEqualToString: @"true"]
|
||||
&& ![component userIsOrganizer: ownerUser])
|
||||
rc = 1;
|
||||
rc = componentOwnerIsInvited;
|
||||
else
|
||||
rc = 2;
|
||||
rc = componentReadableOnly;
|
||||
}
|
||||
else
|
||||
rc = 2; // not invited, just RO
|
||||
rc = componentReadableOnly;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getEventRWType
|
||||
{
|
||||
SOGoContentObject <SOGoComponentOccurence> *clientObject;
|
||||
SOGoUser *ownerUser;
|
||||
int rc;
|
||||
SOGoContentObject <SOGoComponentOccurence> *clientObject;
|
||||
SOGoUser *ownerUser;
|
||||
int rc;
|
||||
|
||||
clientObject = [self clientObject];
|
||||
ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context]];
|
||||
if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]])
|
||||
rc = 2;
|
||||
else
|
||||
{
|
||||
if ([ownerUser isEqual: [context activeUser]])
|
||||
rc = [self ownerIsAttendee: ownerUser
|
||||
andClientObject: clientObject];
|
||||
else
|
||||
rc = [self delegateIsAttendee: ownerUser
|
||||
andClientObject: clientObject];
|
||||
}
|
||||
clientObject = [self clientObject];
|
||||
ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context]];
|
||||
if ([clientObject isKindOfClass: [SOGoWebAppointmentFolder class]])
|
||||
rc = componentReadableOnly;
|
||||
else
|
||||
{
|
||||
if ([ownerUser isEqual: [context activeUser]])
|
||||
rc = [self ownerIsAttendee: ownerUser
|
||||
andClientObject: clientObject];
|
||||
else
|
||||
rc = [self delegateIsAttendee: ownerUser
|
||||
andClientObject: clientObject];
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) isReadOnly
|
||||
{
|
||||
return [self getEventRWType] != 0;
|
||||
return [self getEventRWType] != componentReadableWritable;
|
||||
}
|
||||
//
|
||||
//- (NSString *) emailAlarmsEnabled
|
||||
@@ -862,10 +873,10 @@ static NSArray *reminderValues = nil;
|
||||
// : @"false");
|
||||
//}
|
||||
|
||||
//- (BOOL) userHasRSVP
|
||||
//{
|
||||
// return ([self getEventRWType] == 1);
|
||||
//}
|
||||
- (BOOL) userHasRSVP
|
||||
{
|
||||
return ([self getEventRWType] == componentOwnerIsInvited);
|
||||
}
|
||||
|
||||
//- (unsigned int) firstDayOfWeek
|
||||
//{
|
||||
|
||||
Reference in New Issue
Block a user