diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h index cac24a66c..02de57912 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -35,7 +35,7 @@ extern NSNumber *iCalDistantFutureNumber; + (void) initializeSOGoExtensions; -- (NSDictionary *) attributes; +- (NSDictionary *) attributesInContext: (WOContext *) context; - (void) setAttributes: (NSDictionary *) data inContext: (WOContext *) context; @@ -44,7 +44,6 @@ extern NSNumber *iCalDistantFutureNumber; - (iCalPerson *) userAsAttendee: (SOGoUser *) user; -- (NSArray *) attendeeUIDs; - (BOOL) isStillRelevant; - (id) itipEntryWithMethod: (NSString *) method; @@ -52,7 +51,7 @@ extern NSNumber *iCalDistantFutureNumber; - (NSArray *) attendeesWithoutUser: (SOGoUser *) user; - (int) priorityNumber; -- (NSString *) createdBy; +- (NSDictionary *) createdBy; - (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date withOffset: (int) offset diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index 3a6c94c10..a3ffd4c05 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -44,11 +44,13 @@ #import #import +#import #import #import +#import #import "iCalPerson+SOGo.h" - +#import "iCalAlarm+SOGo.h" #import "iCalCalendar+SOGo.h" #import "iCalEntityObject+SOGo.h" @@ -70,18 +72,23 @@ NSNumber *iCalDistantFutureNumber = nil; /** * @see [UIxAppointmentEditor viewAction] */ -- (NSDictionary *) attributes +- (NSDictionary *) attributesInContext: (WOContext *) context { NSArray *elements; NSMutableArray *attendees; - NSDictionary *organizerData; - NSMutableDictionary *data, *attendeeData, *alarmData; + NSDictionary *contactData; + NSMutableDictionary *data, *organizerData, *attendeeData, *alarmData; NSEnumerator *attendeesList; + NSString *uid, *domain, *sentBy; + NSObject *source; + SOGoUserManager *um; iCalPerson *organizer, *currentAttendee; iCalAlarm *alarm; iCalTrigger *trigger; id value; + um = [SOGoUserManager sharedUserManager]; + data = [NSMutableDictionary dictionaryWithObjectsAndKeys: [[self tag] lowercaseString], @"component", [self summary], @"summary", @@ -90,33 +97,46 @@ NSNumber *iCalDistantFutureNumber = nil; value = [self location]; if (value) [data setObject: value forKey: @"location"]; - if ([self comment]) [data setObject: [self comment] forKey: @"comment"]; - if ([self attach]) [data setObject: [[self attach] absoluteString] forKey: @"attachUrl"]; - if ([self accessClass]) [data setObject: [[self accessClass] lowercaseString] forKey: @"classification"]; - if ([self status]) [data setObject: [self status] forKey: @"status"]; - if ([self createdBy]) [data setObject: [self createdBy] forKey: @"createdBy"]; + + value = [self comment]; + if (value) [data setObject: value forKey: @"comment"]; + + value = [self attach]; + if (value) [data setObject: [value absoluteString] forKey: @"attachUrl"]; + + value = [self accessClass]; + if (value) [data setObject: [value lowercaseString] forKey: @"classification"]; + + value = [self status]; + if (value) [data setObject: value forKey: @"status"]; + + value = [self createdBy]; + if (value) [data setObject: value forKey: @"createdBy"]; // Categories elements = [self categories]; if ([elements count]) [data setObject: elements forKey: @"categories"]; - // Send appointment notifications + // Send appointment notifications when the custom tag is *not* set value = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"]; - [data setObject: [NSNumber numberWithBool: (value? 1:0)] forKey: @"sendAppointmentNotifications"]; + [data setObject: [NSNumber numberWithBool: (value? 0:1)] forKey: @"sendAppointmentNotifications"]; // Organizer organizer = [self organizer]; if (organizer) { - organizerData = [NSDictionary dictionaryWithObjectsAndKeys: - [organizer rfc822Email], @"email", - [organizer cnWithoutQuotes], @"name", - nil]; + organizerData = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [organizer rfc822Email], @"email", + [organizer cnWithoutQuotes], @"name", + nil]; + uid = [organizer uid]; + if ([uid length]) [organizerData setObject: uid forKey: @"uid"]; + sentBy = [organizer sentBy]; + if ([sentBy length]) [organizerData setObject: sentBy forKey: @"sentBy"]; [data setObject: organizerData forKey: @"organizer"]; } - // Attendees attendees = [NSMutableArray array]; attendeesList = [[self attendees] objectEnumerator]; @@ -126,29 +146,28 @@ NSNumber *iCalDistantFutureNumber = nil; [currentAttendee rfc822Email], @"email", [currentAttendee cnWithoutQuotes], @"name", nil]; - if ([currentAttendee uid]) [attendeeData setObject: [currentAttendee uid] forKey: @"uid"]; - // TODO: restore support for MS Exchange - // uid = [um getUIDForEmail: [currentAttendee rfc822Email]]; - // if (uid != nil) - // [currentAttendeeData setObject: uid - // forKey: @"uid"]; - // else - // { - // domain = [[context activeUser] domain]; - // contacts = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain]; - // if ([contacts count] == 1) - // { - // contact = [contacts lastObject]; - // source = [contact objectForKey: @"source"]; - // if ([source conformsToProtocol: @protocol (SOGoDNSource)] && - // [[(NSObject *) source MSExchangeHostname] length]) - // { - // uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login], - // [contact valueForKey: @"c_uid"]]; - // [currentAttendeeData setObject: uid forKey: @"uid"]; - // } - // } - // } + if ((uid = [currentAttendee uid])) + { + [attendeeData setObject: uid forKey: @"uid"]; + } + else + { + // Search for attendee in global contacts sources that are associated with a MS Exchange server + domain = [[context activeUser] domain]; + elements = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain]; + if ([elements count] == 1) + { + contactData = [elements lastObject]; + source = [contactData objectForKey: @"source"]; + if ([source conformsToProtocol: @protocol (SOGoDNSource)] && + [[(NSObject *) source MSExchangeHostname] length]) + { + uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login], + [contactData valueForKey: @"c_uid"]]; + [attendeeData setObject: uid forKey: @"uid"]; + } + } + } [attendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"status"]; [attendeeData setObject: [[currentAttendee role] lowercaseString] forKey: @"role"]; if ([[currentAttendee delegatedTo] length]) @@ -193,6 +212,177 @@ NSNumber *iCalDistantFutureNumber = nil; return data; } +- (void) _setAttendees: (NSArray *) attendees +{ + NSMutableArray *newAttendees; + NSUInteger count, max; + NSString *currentEmail; + iCalPerson *currentAttendee; + NSString *role, *partstat; + NSDictionary *currentData; + + if (attendees) + { + newAttendees = [NSMutableArray array]; + max = [attendees count]; + for (count = 0; count < max; count++) + { + currentData = [attendees objectAtIndex: count]; + currentEmail = [currentData objectForKey: @"email"]; + if ([currentEmail length] > 0) + { + 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 = [self findAttendeeWithEmail: currentEmail]; + if (!currentAttendee) + { + currentAttendee = [iCalPerson elementWithTag: @"attendee"]; + [currentAttendee setCn: [currentData objectForKey: @"name"]]; + [currentAttendee setEmail: currentEmail]; + } + [currentAttendee + setRsvp: ([role isEqualToString: @"NON-PARTICIPANT"] + ? @"FALSE" + : @"TRUE")]; + [currentAttendee setRole: role]; + [currentAttendee setPartStat: partstat]; + [newAttendees addObject: currentAttendee]; + } + } + [self setAttendees: newAttendees]; + } +} + +- (void) _appendAttendeesToEmailAlarm: (iCalAlarm *) alarm +{ + NSArray *attendees; + NSMutableArray *aAttendees; + int count, max; + iCalPerson *currentAttendee, *aAttendee; + + attendees = [self attendees]; + max = [attendees count]; + aAttendees = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + currentAttendee = [attendees objectAtIndex: count]; + aAttendee = [iCalPerson elementWithTag: @"attendee"]; + [aAttendee setCn: [currentAttendee cn]]; + [aAttendee setEmail: [currentAttendee rfc822Email]]; + [aAttendees addObject: aAttendee]; + } + [alarm setAttendees: aAttendees]; +} + +- (void) _setAlarm: (NSDictionary *) alarm + forOwner: (NSString *) owner +{ + iCalAlarm *anAlarm; + NSString *reminderAction, *reminderUnit, *reminderQuantity, *reminderReference, *reminderRelation; + BOOL reminderEmailAttendees, reminderEmailOrganizer; + + 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 + owner: owner + action: reminderAction + unit: reminderUnit + quantity: reminderQuantity + reference: reminderReference + reminderRelation: reminderRelation + emailAttendees: reminderEmailAttendees + emailOrganizer: reminderEmailOrganizer]; + + // If there was an unsupported alarm defined in the event, it will be deleted. + [self removeAllAlarms]; + if (anAlarm) + { + [self addToAlarms: anAlarm]; + [anAlarm release]; + } +} + +/** + * @see [UIxAppointmentEditor saveAction] + */ +- (void) setAttributes: (NSDictionary *) data + inContext: (WOContext *) context +{ + NSString *owner; + id o, sendAppointmentNotifications; + + o = [data objectForKey: @"summary"]; + if ([o isKindOfClass: [NSString class]]) + [self setSummary: o]; + + o = [data objectForKey: @"priority"]; + if ([o isKindOfClass: [NSNumber class]]) + [self setPriority: [NSString stringWithFormat: @"%d", [o intValue]]]; + + o = [data objectForKey: @"location"]; + if ([o isKindOfClass: [NSString class]]) + [self setLocation: o]; + + o = [data objectForKey: @"comment"]; + if ([o isKindOfClass: [NSString class]]) + [self setComment: [o stringByReplacingString: @"\r\n" withString: @"\n"]]; + + o = [data objectForKey: @"attachUrl"]; + if ([o isKindOfClass: [NSString class]]) + [self setAttach: o]; + + o = [data objectForKey: @"classification"]; + if ([o isKindOfClass: [NSString class]]) + [self setAccessClass: [o uppercaseString]]; + + o = [data objectForKey: @"status"]; + if ([o isKindOfClass: [NSString class]]) + [self setStatus: o]; + + o = [data objectForKey: @"categories"]; + if ([o isKindOfClass: [NSArray class]]) + [self setCategories: o]; + + o = [data objectForKey: @"sendAppointmentNotifications"]; + if ([o isKindOfClass: [NSNumber class]]) + { + sendAppointmentNotifications = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"]; + if (!sendAppointmentNotifications && ![o boolValue]) + [self addChild: [CardElement simpleElementWithTag: @"X-SOGo-Send-Appointment-Notifications" value: @"NO"]]; + else if (sendAppointmentNotifications && [o boolValue]) + [self removeChild: sendAppointmentNotifications]; + } + + o = [data objectForKey: @"attendees"]; + if ([o isKindOfClass: [NSArray class]]) + [self _setAttendees: o]; + + o = [data objectForKey: @"alarm"]; + if ([o isKindOfClass: [NSDictionary class]]) + { + owner = [data objectForKey: @"owner"]; + [self _setAlarm: o forOwner: owner]; + } + + // Other attributes depend on the client object and therefore are set in [UIxComponentEditor setAttributes:]: + // - organizer & "created-by" + // - timestamps (creation/modification) +} + - (BOOL) userIsAttendee: (SOGoUser *) user { NSEnumerator *attendees; @@ -243,10 +433,11 @@ NSNumber *iCalDistantFutureNumber = nil; if ([mail length]) return [user hasEmail: mail]; - + return NO; } +/* - (NSArray *) attendeeUIDs { NSEnumerator *attendees; @@ -266,6 +457,7 @@ NSNumber *iCalDistantFutureNumber = nil; return uids; } +*/ #warning this method should be implemented in a category of iCalToDo - (BOOL) isStillRelevant @@ -282,11 +474,11 @@ NSNumber *iCalDistantFutureNumber = nil; NSArray *events; int i, count; - + newCalendar = [parent mutableCopy]; [newCalendar autorelease]; [newCalendar setMethod: method]; - + events = [newCalendar childrenWithTag: tag]; count = [events count]; if (count > 1) @@ -379,19 +571,39 @@ NSNumber *iCalDistantFutureNumber = nil; return priorityNumber; } -- (NSString *) createdBy +- (NSDictionary *) createdBy { - NSString *created_by; + NSString *created_by, *created_by_name, *login; + NSDictionary *createdByData; + SOGoUser *user; + created_by_name = nil; + createdByData = nil; created_by = [[self firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]; - - // We consider "SENT-BY" in case our custom header isn't found + if (![created_by length]) { + // We consider "SENT-BY" in case our custom header isn't found created_by = [[self organizer] sentBy]; } - return created_by; + if ([created_by length]) + { + login = [[SOGoUserManager sharedUserManager] getUIDForEmail: created_by]; + if (login) + { + user = [SOGoUser userWithLogin: login]; + if (user) + created_by_name = [user cn]; + } + + createdByData = [NSDictionary dictionaryWithObjectsAndKeys: created_by, @"email", + created_by_name, @"name", + nil]; + } + + return createdByData; +; } // @@ -478,20 +690,20 @@ NSNumber *iCalDistantFutureNumber = nil; NSCalendarDate *start, *end; NGCalendarDateRange *range; NSMutableArray *alarms; - + alarms = [NSMutableArray array]; start = [NSCalendarDate date]; end = [start addYear:1 month:0 day:0 hour:0 minute:0 second:0]; range = [NGCalendarDateRange calendarDateRangeWithStartDate: start endDate: end]; - + // Always check if container is defined. If not, that means this method // call was reentrant. if (theContainer) { NSTimeInterval now; int i, v, delta, c_startdate, c_nextalarm; - + // // Here is the logic: // @@ -506,12 +718,12 @@ NSNumber *iCalDistantFutureNumber = nil; // If we don't have a match, we pick the event for which its trigger is the closest to the c_nextalarm. // nextAlarmDate = nil; - + [theContainer flattenCycleRecord: (id)row forRange: range intoArray: alarms withCalendar: [self parent]]; - + // We pickup the closest one from now. We remove the actual reminder (ie., 15 mins before - plus 1 minute for roundups) // so we don't pickup the same alarm over and over. This could happen if our alarm popups and the user clicks on "Cancel". // In case of a repetitive event, we want to pickup the next one (next day for example), and not the one that has just @@ -523,11 +735,11 @@ NSNumber *iCalDistantFutureNumber = nil; c_startdate = [[[alarms objectAtIndex: i] objectForKey: @"c_startdate"] intValue]; c_nextalarm = [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]; delta = (c_startdate - now - (c_startdate - c_nextalarm)) - 60; - + // If value is not initialized, we grab it right away if (!v && delta > 0) v = delta; - + // If we found a smaller delta than before, use it. if (v > 0 && delta > 0 && delta <= v) { @@ -538,10 +750,10 @@ NSNumber *iCalDistantFutureNumber = nil; o = [[self parent] eventWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]]; else o = [[self parent] todoWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]]; - + if (!o) o = self; - + if ([[o alarms] count]) { anAlarm = [self firstDisplayOrAudioAlarm]; @@ -551,7 +763,7 @@ NSNumber *iCalDistantFutureNumber = nil; if (!webstatus || ([webstatus caseInsensitiveCompare: @"TRIGGERED"] != NSOrderedSame)) - + v = delta; nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]]; } @@ -565,7 +777,7 @@ NSNumber *iCalDistantFutureNumber = nil; } // if (theContainer) } } - + if ([nextAlarmDate isNotNull]) [row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]] forKey: @"c_nextalarm"]; diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index d04d89427..e0172b8a3 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -1,6 +1,6 @@ /* iCalEvent+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * 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 @@ -28,7 +28,7 @@ @interface iCalEvent (SOGoExtensions) - (BOOL) isStillRelevant; -- (unsigned int) occurenceInterval; +- (NSTimeInterval) occurenceInterval; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; @end diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index ba1032d2f..25249435c 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -1,6 +1,6 @@ /* iCalEvent+SOGo.m - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * 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 @@ -22,12 +22,14 @@ #import #import #import +#import #import #import #import #import +#import #import #import #import @@ -36,6 +38,10 @@ #import #import +#import +#import +#import + #import "SOGoAppointmentFolder.h" #import "iCalRepeatableEntityObject+SOGo.h" @@ -47,7 +53,7 @@ { NSCalendarDate *now, *lastRecurrence; BOOL isStillRelevent; - + now = [NSCalendarDate calendarDate]; if ([self isRecurrent]) @@ -57,7 +63,7 @@ } else isStillRelevent = ([[self endDate] earlierDate: now] == now); - + return isStillRelevent; } @@ -114,8 +120,8 @@ row = [NSMutableDictionary dictionaryWithCapacity:8]; [row setObject: @"vevent" forKey: @"c_component"]; - - if ([uid isNotNull]) + + if ([uid isNotNull]) [row setObject:uid forKey: @"c_uid"]; else [self logWithFormat: @"WARNING: could not extract a uid from event!"]; @@ -168,11 +174,11 @@ forAllDay: isAllDay] forKey: @"c_enddate"]; } - + if ([self isRecurrent]) { NSCalendarDate *date; - + date = [self lastPossibleRecurrenceStartDate]; if (!date) { @@ -194,7 +200,7 @@ if ([status isNotNull]) { int code = 1; - + if ([status isEqualToString: @"TENTATIVE"]) code = 2; else if ([status isEqualToString: @"CANCELLED"]) @@ -214,12 +220,12 @@ if (organizer) { NSString *email; - + email = [organizer valueForKey: @"rfc822Email"]; if (email) [row setObject:email forKey: @"c_orgmail"]; } - + /* construct partstates */ count = [attendees count]; partstates = [[NSMutableString alloc] initWithCapacity:count * 2]; @@ -227,7 +233,7 @@ { iCalPerson *p; iCalPersonPartStat stat; - + p = [attendees objectAtIndex:i]; stat = [p participationStatus]; if (i) @@ -263,7 +269,7 @@ } /** - * Shift the "until dates" of the recurrence rules of the event + * Shift the "until dates" of the recurrence rules of the event * with respect to the previous end date of the event. * @param previousEndDate the previous end date of the event */ @@ -312,12 +318,65 @@ } } } - + +// From [UIxDatePicker takeValuesFromRequest:inContext:] +- (NSCalendarDate *) _dateFromString: (NSString *) dateString + inContext: (WOContext *) context +{ + NSInteger dateTZOffset, userTZOffset; + NSTimeZone *systemTZ, *userTZ; + SOGoUserDefaults *ud; + NSCalendarDate *date; + + date = [NSCalendarDate dateWithString: dateString + calendarFormat: @"%Y-%m-%d"]; + if (!date) + [self warnWithFormat: @"Could not parse dateString: '%@'", dateString]; + + // We must adjust the date timezone because "dateWithString:..." uses the + // system timezone, which can be different from the user's. */ + ud = [[context activeUser] userDefaults]; + systemTZ = [date timeZone]; + dateTZOffset = [systemTZ secondsFromGMTForDate: date]; + userTZ = [ud timeZone]; + userTZOffset = [userTZ secondsFromGMTForDate: date]; + if (dateTZOffset != userTZOffset) + date = [date dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: (dateTZOffset - userTZOffset)]; + [date setTimeZone: userTZ]; + + return date; +} + +// From [UIxTimeDatePicker takeValuesFromRequest:inContext:] +- (void) _adjustDate: (NSCalendarDate **) date + withTimeString: (NSString *) timeString + inContext: (WOContext *) context +{ + unsigned _year, _month, _day, _hour, _minute; + SOGoUserDefaults *ud; + NSArray *_time; + + _year = [*date yearOfCommonEra]; + _month = [*date monthOfYear]; + _day = [*date dayOfMonth]; + _time = [timeString componentsSeparatedByString: @":"]; + _hour = [[_time objectAtIndex: 0] intValue]; + _minute = [[_time objectAtIndex: 1] intValue]; + + ud = [[context activeUser] userDefaults]; + *date = [NSCalendarDate dateWithYear: _year month: _month day: _day + hour: _hour minute: _minute second: 0 + timeZone: [ud timeZone]]; +} + /** + * @see [iCalRepeatableEntityObject+SOGo attributes] * @see [iCalEntityObject+SOGo attributes] * @see [UIxAppointmentEditor viewAction] */ -- (NSDictionary *) attributes +- (NSDictionary *) attributesInContext: (WOContext *) context { NSMutableDictionary *data; @@ -329,4 +388,100 @@ return data; } +/** + * @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:] + * @see [iCalEntityObject+SOGo setAttributes:inContext:] + * @see [UIxAppointmentEditor saveAction] + */ +- (void) setAttributes: (NSDictionary *) data + inContext: (WOContext *) context +{ + NSCalendarDate *aptStartDate, *aptEndDate, *allDayStartDate; + NSTimeZone *timeZone; + NSInteger offset, nbrDays; + iCalDateTime *startDate; + iCalTimeZone *tz; + SOGoUserDefaults *ud; + BOOL isAllDay; + id o; + + [super setAttributes: data inContext: context]; + + aptStartDate = aptEndDate = nil; + + // Handle start/end dates + o = [data objectForKey: @"startDate"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + aptStartDate = [self _dateFromString: o inContext: context]; + + o = [data objectForKey: @"startTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + [self _adjustDate: &aptStartDate withTimeString: o inContext: context]; + + o = [data objectForKey: @"endDate"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + aptEndDate = [self _dateFromString: o inContext: context]; + + o = [data objectForKey: @"endTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + [self _adjustDate: &aptEndDate withTimeString: o inContext: context]; + + o = [data objectForKey: @"isTransparent"]; + if ([o isKindOfClass: [NSNumber class]]) + [self setTransparency: ([o boolValue]? @"TRANSPARENT" : @"OPAQUE")]; + + isAllDay = [[data objectForKey: @"isAllDay"] boolValue]; + + if (aptStartDate && aptEndDate) + { + if (isAllDay) + { + nbrDays = ((float) abs ([aptEndDate timeIntervalSinceDate: aptStartDate]) / 86400) + 1; + // Convert all-day start date to GMT (floating date) + ud = [[context activeUser] userDefaults]; + timeZone = [ud timeZone]; + offset = [timeZone secondsFromGMTForDate: aptStartDate]; + allDayStartDate = [aptStartDate dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: offset]; + [self setAllDayWithStartDate: allDayStartDate + duration: nbrDays]; + } + else + { + [self setStartDate: aptStartDate]; + [self setEndDate: aptEndDate]; + } + } + + if (!isAllDay) + { + // Make sure there's a vTimeZone associated to the event unless it + // is an all-day event. + startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + if (![startDate timeZone]) + { + ud = [[context activeUser] userDefaults]; + tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; + if ([[self parent] addTimeZone: tz]) + { + [startDate setTimeZone: tz]; + [(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: tz]; + } + } + } + else // if (![[self clientObject] isNew]) + { + // Remove the vTimeZone when dealing with an all-day event. + startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + tz = [startDate timeZone]; + if (tz) + { + [startDate setTimeZone: nil]; + [(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: nil]; + [[self parent] removeChild: tz]; + } + } +} + @end diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index 0e6856932..43e42a7ab 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -37,6 +37,10 @@ #import +#import +#import +#import + #import "iCalRepeatableEntityObject+SOGo.h" @implementation iCalRepeatableEntityObject (SOGoExtensions) @@ -76,7 +80,7 @@ * @see [iCalEvent+SOGo attributes] * @see [UIxAppointmentEditor viewAction] */ -- (NSDictionary *) attributes +- (NSDictionary *) attributesInContext: (WOContext *) context { NSMutableDictionary *data, *repeat; NSArray *rules; @@ -110,6 +114,103 @@ return data; } +/** + * @see [iCalEntityObject+SOGo setAttributes:inContext:] + * @see [UIxAppointmentEditor saveAction] + */ +- (void) setAttributes: (NSDictionary *) data + inContext: (WOContext *) context +{ + iCalRecurrenceRule *rule; + iCalRecurrenceFrequency frequency; + NSCalendarDate *date; + SOGoUserDefaults *ud; + id repeat, o; + + [super setAttributes: data inContext: context]; + + if ([self recurrenceId]) + // Occurrence of a recurrent object can't have a recurrence rule + return; + + repeat = [data objectForKey: @"repeat"]; + if ([repeat isKindOfClass: [NSDictionary class]]) + { + rule = [iCalRecurrenceRule new]; + [rule setInterval: @"1"]; + + frequency = NSNotFound; + o = [repeat objectForKey: @"frequency"]; + if ([o isKindOfClass: [NSString class]]) + { + frequency = [rule valueForFrequency: o]; + if ((NSUInteger) frequency == NSNotFound) + { + if ([o caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame) + { + frequency = iCalRecurrenceFrequenceWeekly; + [rule setInterval: @"2"]; + } + else if ([o caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame) + { + frequency = iCalRecurrenceFrequenceDaily; + [rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]]; + } + } + else + { + o = [repeat objectForKey: @"interval"]; + if ([o isKindOfClass: [NSNumber class]]) + [rule setInterval: [NSString stringWithFormat: @"%i", [o intValue]]]; + + o = [repeat objectForKey: @"count"]; + if ([o isKindOfClass: [NSNumber class]]) + [rule setRepeatCount: [o intValue]]; + + o = [repeat objectForKey: @"until"]; + if ([o isKindOfClass: [NSString class]]) + { + date = [NSCalendarDate dateWithString: o + calendarFormat: @"%Y-%m-%d"]; + if (date) + { + // Adjust timezone + ud = [[context activeUser] userDefaults]; + date = [NSCalendarDate dateWithYear: [date yearOfCommonEra] + month: [date monthOfYear] + day: [date dayOfMonth] + hour: 0 minute: 0 second: 0 + timeZone: [ud timeZone]]; + [rule setUntilDate: date]; + } + } + + o = [repeat objectForKey: @"days"]; + if ([o isKindOfClass: [NSArray class]]) + [rule setByDayMask: [iCalByDayMask byDayMaskWithDaysAndOccurences: o]]; + + o = [repeat objectForKey: @"monthdays"]; + if ([o isKindOfClass: [NSArray class]]) + [rule setValues: o atIndex: 0 forKey: @"bymonthday"]; + + o = [repeat objectForKey: @"months"]; + if ([o isKindOfClass: [NSArray class]]) + [rule setValues: o atIndex: 0 forKey: @"bymonth"]; + } + + if ((NSUInteger) frequency != NSNotFound) + [rule setFrequency: frequency]; + + [self setRecurrenceRules: [NSArray arrayWithObject: rule]]; + [rule release]; + } + } + else if ([self hasRecurrenceRules]) + { + [self removeAllRecurrenceRules]; + } +} + - (NSString *) cycleInfo { NSArray *rules; @@ -124,7 +225,7 @@ rules = [self _indexedRules: [self recurrenceRules]]; if (rules) [cycleInfo setObject: rules forKey: @"rules"]; - + rules = [self _indexedRules: [self exceptionRules]]; if (rules) [cycleInfo setObject: rules forKey: @"exRules"]; @@ -163,7 +264,7 @@ firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start endDate: end]; } - + return firstRange; } diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index 2a85a6edb..a75adfecb 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -301,13 +301,6 @@ } else data = @""; - else if ([data isKindOfClass: [NSArray class]]) - { - if ([data count] > 0) - data = [data objectAtIndex: 0]; - else - data = @""; - } [newRecord setObject: data forKey: @"c_telephonenumber"]; // Custom attribute for group-lookups. See LDAPSource.m where diff --git a/UI/Scheduler/UIxAppointmentEditor.h b/UI/Scheduler/UIxAppointmentEditor.h index bdb4f1df8..ece2c9c6d 100644 --- a/UI/Scheduler/UIxAppointmentEditor.h +++ b/UI/Scheduler/UIxAppointmentEditor.h @@ -1,6 +1,6 @@ /* UIxAppointmentEditor.h - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * 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 @@ -24,43 +24,14 @@ #import @class iCalEvent; -@class NSString; -@interface UIxAppointmentEditor : UIxComponent +@interface UIxAppointmentEditor : UIxComponentEditor { - iCalEvent *event; - BOOL isAllDay, isTransparent, sendAppointmentNotifications; - NSCalendarDate *aptStartDate; - NSCalendarDate *aptEndDate; - NSString *item; - SOGoAppointmentFolder *componentCalendar; SOGoDateFormatter *dateFormatter; } -/* template values */ -- (NSString *) saveURL; - (iCalEvent *) event; -/* icalendar values */ -- (void) setIsAllDay: (BOOL) newIsAllDay; -- (BOOL) isAllDay; - -- (void) setIsTransparent: (BOOL) newIsOpaque; -- (BOOL) isTransparent; - -- (void) setSendAppointmentNotifications: (BOOL) theBOOL; -- (BOOL) sendAppointmentNotifications; - -- (void) setAptStartDate: (NSCalendarDate *) newAptStartDate; -- (NSCalendarDate *) aptStartDate; - -- (void) setAptEndDate: (NSCalendarDate *) newAptEndDate; -- (NSCalendarDate *) aptEndDate; - -- (NSString *) aptStartDateText; -- (NSString *) aptStartDateTimeText; -- (NSString *) aptEndDateTimeText; - @end #endif /* UIXAPPOINTMENTEDITOR_H */ diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 35a314da2..d90a045ae 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -33,6 +33,7 @@ #import #import #import +#import #import #import @@ -73,15 +74,6 @@ if ((self = [super init])) { - aptStartDate = nil; - aptEndDate = nil; - item = nil; - event = nil; - isAllDay = NO; - isTransparent = NO; - sendAppointmentNotifications = YES; - componentCalendar = nil; - user = [[self context] activeUser]; ASSIGN (dateFormatter, [user dateFormatterInContext: context]); } @@ -91,116 +83,16 @@ - (void) dealloc { - [item release]; - [[event parent] release]; - [aptStartDate release]; - [aptEndDate release]; [dateFormatter release]; - [componentCalendar release]; [super dealloc]; } -/* template values */ - (iCalEvent *) event { - if (!event) - { - event = (iCalEvent *) [[self clientObject] occurence]; - [[event parent] retain]; - } - - return event; + return (iCalEvent *) component; } -- (NSString *) rsvpURL -{ - return [NSString stringWithFormat: @"%@/rsvpAppointment", - [[self clientObject] baseURL]]; -} -- (NSString *) saveURL -{ - return [NSString stringWithFormat: @"%@/saveAsAppointment", - [[self clientObject] baseURL]]; -} - -/* icalendar values */ -- (BOOL) isAllDay -{ - NSString *hm; - - hm = [self queryParameterForKey: @"hm"]; - - return (isAllDay - || [hm isEqualToString: @"allday"]); -} - -- (void) setIsAllDay: (BOOL) newIsAllDay -{ - isAllDay = newIsAllDay; -} - -- (BOOL) isTransparent -{ - return isTransparent; -} - -- (void) setIsTransparent: (BOOL) newIsTransparent -{ - isTransparent = newIsTransparent; -} - -- (void) setSendAppointmentNotifications: (BOOL) theBOOL -{ - sendAppointmentNotifications = theBOOL; -} - -- (BOOL) sendAppointmentNotifications -{ - return sendAppointmentNotifications; -} - - -- (void) setAptStartDate: (NSCalendarDate *) newAptStartDate -{ - ASSIGN (aptStartDate, newAptStartDate); -} - -- (NSCalendarDate *) aptStartDate -{ - return aptStartDate; -} - -- (void) setAptEndDate: (NSCalendarDate *) newAptEndDate -{ - ASSIGN (aptEndDate, newAptEndDate); -} - -- (NSCalendarDate *) aptEndDate -{ - return aptEndDate; -} - -- (void) setItem: (NSString *) newItem -{ - ASSIGN (item, newItem); -} - -- (NSString *) item -{ - return item; -} - -- (SOGoAppointmentFolder *) componentCalendar -{ - return componentCalendar; -} - -- (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar -{ - ASSIGN (componentCalendar, _componentCalendar); -} - -- (NSString *) formattedDateString: (NSCalendarDate *) date +- (NSString *) _dateString: (NSCalendarDate *) date { char buf[22]; NSNumber *day, *month, *year; @@ -217,14 +109,7 @@ return [NSString stringWithCString:buf]; } - -/* read-only event */ -- (BOOL) startDateIsEqualToEndDate -{ - return [aptStartDate isEqualToDate: aptEndDate]; -} - -/* actions */ +/* - (NSCalendarDate *) newStartDate { NSCalendarDate *newStartDate, *now; @@ -262,7 +147,9 @@ return newStartDate; } +*/ +/* - (id ) defaultAction { NSCalendarDate *startDate, *endDate; @@ -316,7 +203,7 @@ endDate = [endDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset]; } - isTransparent = ![event isOpaque]; + isTransparent = ![event isOpaque]; sendAppointmentNotifications = ([event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"] ? NO : YES); } @@ -328,15 +215,18 @@ return self; } +*/ - (void) _adjustRecurrentRules { + iCalEvent *event; iCalRecurrenceRule *rule; NSEnumerator *rules; NSCalendarDate *untilDate; SOGoUserDefaults *ud; NSTimeZone *timeZone; - + + event = [self event]; rules = [[event recurrenceRules] objectEnumerator]; ud = [[context activeUser] userDefaults]; timeZone = [ud timeZone]; @@ -492,66 +382,151 @@ return response; } -// -// -// +/** + * @api {post} /so/:username/Calendar/:calendarId/:appointmentId/save Save event + * @apiVersion 1.0.0 + * @apiName PostEventSave + * @apiGroup Calendar + * @apiExample {curl} Example usage: + * curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/71B6-54904400-1-7C308500.ics/save \ + * -H 'Content-Type: application/json' \ + * -d '{ "summary": "Meeting", "startDate": "2015-01-28", "startTime": "10:00", \ + * "endDate": "2015-01-28", "endTime": "12:00" }' + * + * Save in [iCalEvent+SOGo setAttributes:inContext:] + * + * @apiParam {String} startDate Start date (YYYY-MM-DD) + * @apiParam {String} startTime Start time (HH:MM) + * @apiParam {String} endDate End date (YYYY-MM-DD) + * @apiParam {String} endTime End time (HH:MM) + * @apiParam {Number} [isAllDay] 1 if event is all-day + * @apiParam {Number} isTransparent 1 if the event is not opaque + * + * Save in [iCalEntityObject+SOGo setAttributes:inContext:] + * + * @apiParam {Number} [sendAppointmentNotifications] 0 if notifications must not be sent + * @apiParam {String} [summary] Summary + * @apiParam {String} [location] Location + * @apiParam {String} [comment] Comment + * @apiParam {String} [status] Status + * @apiParam {String} [attachUrl] Attached URL + * @apiParam {Number} [priority] Priority + * @apiParam {NSString} [classification] Either public, confidential or private + * @apiParam {String[]} [categories] Categories + * @apiParam {Object[]} [attendees] List of attendees + * @apiParam {String} [attendees.name] Attendee's name + * @apiParam {String} attendees.email Attendee's email address + * @apiParam {String} [attendees.uid] System user ID + * @apiParam {String} attendees.status Attendee's participation status + * @apiParam {String} [attendees.role] Either CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT, or NON-PARTICIPANT + * @apiParam {String} [attendees.delegatedTo] User that the original request was delegated to + * @apiParam {String} [attendees.delegatedFrom] User the request was delegated from + * @apiParam {Object[]} [alarm] Alarm definition + * @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 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 + * + * Save in [iCalRepeatbleEntityObject+SOGo setAttributes:inContext:] + * + * @apiParam {Object} [repeat] Recurrence rule definition + * @apiParam {String} repeat.frequency Either daily, every weekday, weekly, bi-weekly, monthly, or yearly + * @apiParam {Number} repeat.interval Intervals the recurrence rule repeats + * @apiParam {String} [repeat.count] Number of occurrences at which to range-bound the recurrence + * @apiParam {String} [repeat.until] A date (YYYY-MM-DD) that bounds the recurrence rule in an inclusive manner + * @apiParam {Object[]} [repeat.days] List of days of the week (by day mask) + * @apiParam {String} [repeat.days.day] Day of the week (SU, MO, TU, WE, TH, FR, SA) + * @apiParam {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (values are -5 to 5) + * @apiParam {Number[]} [repeat.months] List of months of the year (values are 1 to 12) + * @apiParam {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31) + * + * Save in [UIxComponentEditor setAttributes:] + * + * @apiParam {Object} [organizer] Appointment organizer + * @apiParam {String} organizer.name Organizer's name + * @apiParam {String} organizer.email Organizer's email address + * + * @apiError (Error 500) {Object} error The error message + */ - (id ) saveAction { + NSDictionary *params; + NSString *jsonResponse; + NSException *ex; + iCalEvent *event; SOGoAppointmentFolder *previousCalendar; SOGoAppointmentObject *co; - NSString *jsonResponse; SoSecurityManager *sm; - NSException *ex; + WORequest *request; + event = [self event]; co = [self clientObject]; if ([co isKindOfClass: [SOGoAppointmentOccurence class]]) co = [co container]; previousCalendar = [co container]; sm = [SoSecurityManager sharedSecurityManager]; + ex = nil; - - if ([event hasRecurrenceRules]) - [self _adjustRecurrentRules]; - - if ([co isNew]) + request = [context request]; + params = [[request contentAsString] objectFromJSONString]; + if (params == nil) { - if (componentCalendar - && ![[componentCalendar ocsPath] - isEqualToString: [previousCalendar ocsPath]]) - { - // New event in a different calendar -- make sure the user can - // write to the selected calendar since the rights were verified - // on the calendar specified in the URL, not on the selected - // calendar of the popup menu. - if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: componentCalendar - inContext: context]) - co = [componentCalendar lookupName: [co nameInContainer] - inContext: context - acquire: NO]; - } - - // Save the event. - ex = [co saveComponent: event]; + ex = [NSException exceptionWithName: @"JSONParsingException" + reason: @"Can't parse JSON string" + userInfo: nil]; } else { - // The event was modified -- save it. - ex = [co saveComponent: event]; + [self setAttributes: params]; - if (componentCalendar - && ![[componentCalendar ocsPath] - isEqualToString: [previousCalendar ocsPath]]) + if ([event hasRecurrenceRules]) + [self _adjustRecurrentRules]; + + if ([co isNew]) { - // The event was moved to a different calendar. - if (![sm validatePermission: SoPerm_DeleteObjects - onObject: previousCalendar - inContext: context]) + if (componentCalendar + && ![[componentCalendar ocsPath] + isEqualToString: [previousCalendar ocsPath]]) { + // New event in a different calendar -- make sure the user can + // write to the selected calendar since the rights were verified + // on the calendar specified in the URL, not on the selected + // calendar of the popup menu. if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: componentCalendar - inContext: context]) - ex = [co moveToFolder: componentCalendar]; + onObject: componentCalendar + inContext: context]) + co = [componentCalendar lookupName: [co nameInContainer] + inContext: context + acquire: NO]; + } + + // Save the event. + ex = [co saveComponent: event]; + } + else + { + // The event was modified -- save it. + ex = [co saveComponent: event]; + + if (componentCalendar + && ![[componentCalendar ocsPath] + isEqualToString: [previousCalendar ocsPath]]) + { + // The event was moved to a different calendar. + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: previousCalendar + inContext: context]) + { + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: componentCalendar + inContext: context]) + ex = [co moveToFolder: componentCalendar]; + } } } } @@ -565,9 +540,9 @@ else jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: @"success", @"status", nil]; - + return [self responseWithStatus: 200 - andString: [jsonResponse jsonRepresentation]]; + andJSONRepresentation: jsonResponse]; } /** @@ -590,35 +565,41 @@ * @apiSuccess (Success 200) {String} endDate End date (YYYY-MM-DD) * @apiSuccess (Success 200) {String} localizedEndDate Formatted end date * @apiSuccess (Success 200) {String} endTime Formatted end time + * + * From [iCalEvent+SOGo attributes] + * * @apiSuccess (Success 200) {Number} isAllDay 1 if event is all-day * @apiSuccess (Success 200) {Number} isTransparent 1 if the event is not opaque - * @apiSuccess (Success 200) {Number} sendAppointmentNotifications 1 if notifications must be sent * * From [iCalEntityObject+SOGo attributes] * + * @apiSuccess (Success 200) {Number} sendAppointmentNotifications 1 if notifications must be sent * @apiSuccess (Success 200) {String} component "vevent" * @apiSuccess (Success 200) {String} summary Summary - * @apiSuccess (Success 200) {String} location Location - * @apiSuccess (Success 200) {String} comment Comment + * @apiSuccess (Success 200) {String} [location] Location + * @apiSuccess (Success 200) {String} [comment] Comment + * @apiSuccess (Success 200) {String} [status] Status * @apiSuccess (Success 200) {String} [attachUrl] Attached URL - * @apiSuccess (Success 200) {String} createdBy Value of custom header X-SOGo-Component-Created-By or organizer's "SENT-BY" + * @apiSuccess (Success 200) {String} [createdBy] Value of custom header X-SOGo-Component-Created-By or organizer's "SENT-BY" * @apiSuccess (Success 200) {Number} priority Priority - * @apiSuccess (Success 200) {NSString} classification Either public, confidential or private + * @apiSuccess (Success 200) {NSString} [classification] Either public, confidential or private * @apiSuccess (Success 200) {String[]} [categories] Categories * @apiSuccess (Success 200) {Object} [organizer] Appointment organizer - * @apiSuccess (Success 200) {String} organizer.name Organizer's name + * @apiSuccess (Success 200) {String} [organizer.name] Organizer's name * @apiSuccess (Success 200) {String} organizer.email Organizer's email address + * @apiSuccess (Success 200) {String} [organizer.uid] Organizer's user ID + * @apiSuccess (Success 200) {String} [organizer.sentBy] Email address of user that is acting on behalf of the calendar owner * @apiSuccess (Success 200) {Object[]} [attendees] List of attendees * @apiSuccess (Success 200) {String} [attendees.name] Attendee's name * @apiSuccess (Success 200) {String} attendees.email Attendee's email address * @apiSuccess (Success 200) {String} [attendees.uid] System user ID * @apiSuccess (Success 200) {String} attendees.status Attendee's participation status - * @apiSuccess (Success 200) {String} [attendees.role] Attendee's role + * @apiSuccess (Success 200) {String} [attendees.role] Either CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT, or NON-PARTICIPANT * @apiSuccess (Success 200) {String} [attendees.delegatedTo] User that the original request was delegated to * @apiSuccess (Success 200) {String} [attendees.delegatedFrom] User the request was delegated from * @apiSuccess (Success 200) {Object[]} [alarm] Alarm definition * @apiSuccess (Success 200) {String} alarm.action Either display or email - * @apiSuccess (Success 200) {String} alarm.quantity Quantity of units + * @apiSuccess (Success 200) {Number} alarm.quantity Quantity of units * @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 @@ -634,9 +615,9 @@ * @apiSuccess (Success 200) {Number} repeat.interval Intervals the recurrence rule repeats * @apiSuccess (Success 200) {String} [repeat.count] Number of occurrences at which to range-bound the recurrence * @apiSuccess (Success 200) {String} [repeat.until] A Unix epoch value that bounds the recurrence rule in an inclusive manner - * @apiSuccess (Success 200) {Number[]} [repeat.days] List of days of the week + * @apiSuccess (Success 200) {Object[]} [repeat.days] List of days of the week (by day mask) * @apiSuccess (Success 200) {String} repeat.days.day Day of the week (SU, MO, TU, WE, TH, FR, SA) - * @apiSuccess (Success 200) {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (valures are -5 to 5) + * @apiSuccess (Success 200) {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (values are -5 to 5) * @apiSuccess (Success 200) {Number[]} [repeat.months] List of months of the year (values are 1 to 12) * @apiSuccess (Success 200) {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31) */ @@ -648,11 +629,14 @@ SOGoUserDefaults *ud; SOGoCalendarComponent *co; iCalAlarm *anAlarm; + iCalEvent *event; BOOL resetAlarm; unsigned int snoozeAlarm; - [self event]; + // [self component]; + // [self componentCalendar]; + event = [self event]; ud = [[context activeUser] userDefaults]; timeZone = [ud timeZone]; @@ -661,14 +645,6 @@ [eventStartDate setTimeZone: timeZone]; [eventEndDate setTimeZone: timeZone]; co = [self clientObject]; - - if (!componentCalendar) - { - componentCalendar = [co container]; - if ([componentCalendar isKindOfClass: [SOGoCalendarComponent class]]) - componentCalendar = [componentCalendar container]; - [componentCalendar retain]; - } // resetAlarm=yes is set only when we are about to show the alarm popup in the Web // interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the @@ -676,7 +652,7 @@ // If either is set, we must find the right alarm. resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue]; snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue]; - + if (resetAlarm || snoozeAlarm) { iCalEvent *master; @@ -686,7 +662,7 @@ timezone: timeZone startDate: &eventStartDate endDate: &eventEndDate]; - + anAlarm = [event firstDisplayOrAudioAlarm]; if (resetAlarm) @@ -694,7 +670,7 @@ iCalTrigger *aTrigger; aTrigger = [anAlarm trigger]; - [aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"]; + [aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"]; [co saveComponent: master]; } else if (snoozeAlarm) @@ -707,10 +683,10 @@ [co nameInContainer], @"id", [componentCalendar nameInContainer], @"pid", [componentCalendar displayName], @"calendar", - [self formattedDateString: eventStartDate], @"startDate", + [self _dateString: eventStartDate], @"startDate", [dateFormatter formattedDate: eventStartDate], @"localizedStartDate", [dateFormatter formattedTime: eventStartDate], @"startTime", - [self formattedDateString: eventEndDate], @"endDate", + [self _dateString: eventEndDate], @"endDate", [dateFormatter formattedDate: eventEndDate], @"localizedEndDate", [dateFormatter formattedTime: eventEndDate], @"endTime", nil]; @@ -722,78 +698,4 @@ return [self responseWithStatus: 200 andJSONRepresentation: data]; } -- (void) takeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext *) _ctx -{ - int nbrDays; - iCalDateTime *startDate; - iCalTimeZone *tz; - NSCalendarDate *allDayStartDate; - NSTimeZone *timeZone; - SOGoUserDefaults *ud; - signed int offset; - id o; - - [self event]; - [super takeValuesFromRequest: _rq inContext: _ctx]; - - if (isAllDay) - { - nbrDays = ((float) abs ([aptEndDate timeIntervalSinceDate: aptStartDate]) - / 86400) + 1; - // Convert all-day start date to GMT (floating date) - ud = [[context activeUser] userDefaults]; - timeZone = [ud timeZone]; - offset = [timeZone secondsFromGMTForDate: aptStartDate]; - allDayStartDate = [aptStartDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 - seconds:offset]; - [event setAllDayWithStartDate: allDayStartDate - duration: nbrDays]; - } - else - { - [event setStartDate: aptStartDate]; - [event setEndDate: aptEndDate]; - } - - if (!isAllDay) - { - // Make sure there's a vTimeZone associated to the event unless it - // is an all-day event. - startDate = (iCalDateTime *)[event uniqueChildWithTag: @"dtstart"]; - if (![startDate timeZone]) - { - ud = [[context activeUser] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - if ([[event parent] addTimeZone: tz]) - { - [startDate setTimeZone: tz]; - [(iCalDateTime *)[event uniqueChildWithTag: @"dtend"] setTimeZone: tz]; - } - } - } - else if (![[self clientObject] isNew]) - { - // Remove the vTimeZone when dealing with an all-day event. - startDate = (iCalDateTime *)[event uniqueChildWithTag: @"dtstart"]; - tz = [startDate timeZone]; - if (tz) - { - [startDate setTimeZone: nil]; - [(iCalDateTime *)[event uniqueChildWithTag: @"dtend"] setTimeZone: nil]; - [[event parent] removeChild: tz]; - } - } - - [event setTransparency: (isTransparent? @"TRANSPARENT" : @"OPAQUE")]; - - o = [event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"]; - - if (!sendAppointmentNotifications && !o) - [event addChild: [CardElement simpleElementWithTag: @"X-SOGo-Send-Appointment-Notifications" value: @"NO"]]; - else if (sendAppointmentNotifications && o) - [event removeChild: o]; - -} - @end diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h index b660dc436..5ce3d5855 100644 --- a/UI/Scheduler/UIxComponentEditor.h +++ b/UI/Scheduler/UIxComponentEditor.h @@ -23,167 +23,17 @@ #import -@class NSArray; -@class NSCalendarDate; @class NSDictionary; -@class NSString; -@class iCalPerson; -@class iCalRecurrenceRule; @class iCalRepeatableEntityObject; @interface UIxComponentEditor : UIxComponent { iCalRepeatableEntityObject *component; - id item; - id attendee; - - NSString *rsvpURL; - NSString *saveURL; - NSMutableArray *calendarList; - NSDictionary *organizerProfile; - - /* individual values */ - NSCalendarDate *cycleUntilDate; - NSString *title; - NSString *location; SOGoAppointmentFolder *componentCalendar; - NSString *comment; - NSString *attachUrl; - NSString *priority; - NSString *classification; - NSString *status; - NSString *category; - NSArray *categories; - NSDictionary *cycle; - NSString *cycleEnd; - iCalPerson *organizer; - iCalPerson *ownerAsAttendee; - NSString *componentOwner; - NSString *dateFormat; - - NSMutableDictionary *jsonAttendees; - - NSString *reminder; - NSString *reminderQuantity; - NSString *reminderUnit; - NSString *reminderRelation; - NSString *reminderReference; - NSString *reminderAction; - BOOL reminderEmailOrganizer; - BOOL reminderEmailAttendees; - - /* ugly */ - NSString *repeat; - - NSString *repeatType; - NSString *repeat1; - NSString *repeat2; - NSString *repeat3; - NSString *repeat4; - NSString *repeat5; - NSString *repeat6; - NSString *repeat7; - - NSString *range1; - NSString *range2; } -- (NSString *) toolbar; -- (void) setComponent: (iCalRepeatableEntityObject *) newComponent; - -- (void) setSaveURL: (NSString *) newSaveURL; -- (NSString *) saveURL; - -- (void) setItem: (id) _item; -- (id) item; - -- (SOGoAppointmentFolder *) componentCalendar; - -- (NSArray *) calendarList; -- (NSString *) calendarsFoldersList; -- (NSString *) calendarDisplayName; - -- (SOGoAppointmentFolder *) componentCalendar; -- (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar; - -- (NSArray *) categoryList; -- (void) setCategories: (NSArray *) _categories; -- (NSArray *) categories; - -- (NSArray *) priorities; -- (void) setPriority: (NSString *) _priority; -- (NSString *) priority; -- (NSString *) itemPriorityText; - -- (NSArray *) classificationClasses; -- (void) setClassification: (NSString *) _classification; -- (NSString *) classification; -- (NSString *) itemClassificationText; - -- (void) setStatus: (NSString *) _status; -- (NSString *) status; -- (NSString *) itemStatusText; - -- (void) setTitle: (NSString *) _value; -- (NSString *) title; - -- (void) setLocation: (NSString *) _value; -- (NSString *) location; - -- (NSString *) location; - -- (void) setComment: (NSString *) _value; -- (NSString *) comment; - -- (void) setAttach: (NSString *) _attachUrl; -- (NSString *) attach; - -- (BOOL) hasAttendees; -- (BOOL) hasCreatedBy; -- (NSString *) createdBy; -- (NSString *) createdByLink; -- (NSString *) createdByName; - -- (NSString *) jsonAttendees; - -- (NSString *) repeat; -- (void) setRepeat: (NSString *) newRepeat; - -- (NSString *) reminder; -- (void) setReminder: (NSString *) newReminder; - -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -- (NSArray *) cycles; -- (void) setCycle: (NSDictionary *) _cycle; -- (NSDictionary *) cycle; -- (BOOL) hasCycle; -- (NSString *) cycleLabel; -- (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate; -- (NSCalendarDate *) cycleUntilDate; -- (iCalRecurrenceRule *) rrule; -- (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule; -- (NSDictionary *) cycleMatchingRRule: (iCalRecurrenceRule *) _rrule; -- (NSArray *) cycleEnds; -- (void) setCycleEnd: (NSString *) _cycleEnd; -- (NSString *) cycleEnd; -- (BOOL) isCycleEndUntil; -- (void) setIsCycleEndUntil; -- (void) setIsCycleEndNever; -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// - -/* access */ -- (BOOL) canEditComponent; -- (unsigned int) firstDayOfWeek; - -/* helpers */ -- (NSString *) completeURIForMethod: (NSString *) _method; -- (BOOL) isWriteableClientObject; -- (NSException *) validateObjectForStatusChange; +- (void) setAttributes: (NSDictionary *) attributes; + (NSArray *) reminderValues; diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index a7581e57c..c7ef07f2e 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -38,7 +38,6 @@ #import #import - #import #import #import @@ -79,37 +78,6 @@ static NSArray *reminderItems = nil; static NSArray *reminderValues = nil; -#define iREPEAT(X) \ -- (NSString *) repeat##X; \ -- (void) setRepeat##X: (NSString *) theValue - -#define iRANGE(X) \ -- (NSString *) range##X; \ -- (void) setRange##X: (NSString *) theValue - - -@interface UIxComponentEditor (Private) - -iREPEAT(1); -iREPEAT(2); -iREPEAT(3); -iREPEAT(4); -iREPEAT(5); -iREPEAT(6); -iREPEAT(7); -iRANGE(1); -iRANGE(2); - -@end - -#define REPEAT(X) \ -- (NSString *) repeat##X { return repeat##X; } \ -- (void) setRepeat##X: (NSString *) theValue { ASSIGN(repeat##X, theValue); } \ - -#define RANGE(X) \ -- (NSString *) range##X { return range##X; } \ -- (void) setRange##X: (NSString *) theValue { ASSIGN(range##X, theValue); } - @implementation UIxComponentEditor + (void) initialize @@ -160,46 +128,10 @@ iRANGE(2); - (id) init { - UIxDatePicker *datePicker; - if ((self = [super init])) { - // We must instanciate a UIxDatePicker object to retrieve - // the proper date format to use. - datePicker = [[UIxDatePicker alloc] initWithContext: context]; - dateFormat = [datePicker dateFormat]; - [datePicker release]; - component = nil; componentCalendar = nil; - classification = nil; - [self setIsCycleEndNever]; - componentOwner = @""; - organizer = nil; - organizerProfile = nil; - ownerAsAttendee = nil; - attendee = nil; - jsonAttendees = nil; - calendarList = nil; - repeat = nil; - reminder = nil; - reminderQuantity = nil; - reminderUnit = nil; - reminderRelation = nil; - reminderReference = nil; - reminderAction = nil; - reminderEmailOrganizer = NO; - reminderEmailAttendees = NO; - repeatType = nil; - repeat1 = nil; - repeat2 = nil; - repeat3 = nil; - repeat4 = nil; - repeat5 = nil; - repeat6 = nil; - repeat7 = nil; - range1 = nil; - range2 = nil; } return self; @@ -207,1477 +139,294 @@ iRANGE(2); - (void) dealloc { - [item release]; - [cycleUntilDate release]; - [title release]; - [location release]; - [organizer release]; - [organizerProfile release]; - [ownerAsAttendee release]; - [comment release]; - [priority release]; - [classification release]; - [categories release]; - [cycle release]; - [cycleEnd release]; - [attachUrl release]; - [attendee release]; - [jsonAttendees release]; - [calendarList release]; - - [reminder release]; - [reminderQuantity release]; - [reminderUnit release]; - [reminderRelation release]; - [reminderReference release]; - - [repeat release]; - [repeatType release]; - [repeat1 release]; - [repeat2 release]; - [repeat3 release]; - [repeat4 release]; - [repeat5 release]; - [repeat6 release]; - [repeat7 release]; - [range1 release]; - [range2 release]; - [component release]; [componentCalendar release]; [super dealloc]; } -- (void) _loadAttendees +- (void) setClientObject: (id)_client { - NSEnumerator *attendees; - NSMutableDictionary *currentAttendeeData; - NSString *uid, *domain; - NSArray *contacts; - NSDictionary *contact; - iCalPerson *currentAttendee; - SOGoUserManager *um; - NSObject *source; + [super setClientObject: _client]; // WOComponent+SoObjects - jsonAttendees = [NSMutableDictionary new]; - um = [SOGoUserManager sharedUserManager]; + component = [[self clientObject] occurence]; + [[component parent] retain]; - attendees = [[component attendees] objectEnumerator]; - while ((currentAttendee = [attendees nextObject])) - { - currentAttendeeData = [NSMutableDictionary dictionary]; - - if ([[currentAttendee cn] length]) - [currentAttendeeData setObject: [currentAttendee cn] - forKey: @"name"]; - - [currentAttendeeData setObject: [currentAttendee rfc822Email] - forKey: @"email"]; - - uid = [um getUIDForEmail: [currentAttendee rfc822Email]]; - if (uid != nil) - [currentAttendeeData setObject: uid - forKey: @"uid"]; - else - { - domain = [[context activeUser] domain]; - contacts = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain]; - if ([contacts count] == 1) - { - contact = [contacts lastObject]; - source = [contact objectForKey: @"source"]; - if ([source conformsToProtocol: @protocol (SOGoDNSource)] && - [[(NSObject *) source MSExchangeHostname] length]) - { - uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login], - [contact valueForKey: @"c_uid"]]; - [currentAttendeeData setObject: uid forKey: @"uid"]; - } - } - } - - [currentAttendeeData setObject: [[currentAttendee partStat] lowercaseString] - forKey: @"partstat"]; - [currentAttendeeData setObject: [[currentAttendee role] lowercaseString] - forKey: @"role"]; - - if ([[currentAttendee delegatedTo] length]) - [currentAttendeeData setObject: [[currentAttendee delegatedTo] rfc822Email] - forKey: @"delegated-to"]; - - if ([[currentAttendee delegatedFrom] length]) - [currentAttendeeData setObject: [[currentAttendee delegatedFrom] rfc822Email] - forKey: @"delegated-from"]; - - [jsonAttendees setObject: currentAttendeeData - forKey: [currentAttendee rfc822Email]]; - } + componentCalendar = [[self clientObject] container]; + if ([componentCalendar isKindOfClass: [SOGoCalendarComponent class]]) + componentCalendar = [componentCalendar container]; + [componentCalendar retain]; } -- (void) _loadCategories -{ - NSString *simpleCategory; - NSArray *compCategories; +//- (NSString *) title +//{ +// SOGoCalendarComponent *co; +// NSString *tag; +// +// co = [self clientObject]; +// if ([co isNew] && [co isKindOfClass: [SOGoCalendarComponent class]]) +// { +// tag = [co componentTag]; +// if ([tag isEqualToString: @"vevent"]) +// [self setTitle: [self labelForKey: @"New Event"]]; +// else if ([tag isEqualToString: @"vtodo"]) +// [self setTitle: [self labelForKey: @"New Task"]]; +// } +// +// return title; +//} - compCategories = [component categories]; - if ([compCategories count] > 0) - { - simpleCategory = [compCategories objectAtIndex: 0]; - ASSIGN (category, simpleCategory); - } -} +// - (BOOL) canBeOrganizer +// { +// NSString *owner; +// SOGoObject *co; +// SOGoUser *currentUser; +// BOOL hasOrganizer; +// SoSecurityManager *sm; -- (void) _loadRRules -{ - SOGoUserDefaults *ud; +// co = [self clientObject]; +// owner = [co ownerInContext: context]; +// currentUser = [context activeUser]; - // We initialize our repeat ivars - if ([component hasRecurrenceRules]) - { - iCalRecurrenceRule *rule; +// hasOrganizer = ([[organizer value: 0] length] > 0); - [self setRepeat: @"CUSTOM"]; - - rule = [[component recurrenceRules] lastObject]; - - /* DAILY */ - if ([rule frequency] == iCalRecurrenceFrequenceDaily) - { - repeatType = @"0"; - - if ([[rule byDayMask] isWeekDays]) - { - if ([rule isInfinite]) - repeat = @"EVERY WEEKDAY"; - repeat1 = @"1"; - } - else - { - repeat1 = @"0"; - - if ([rule repeatInterval] == 1 && [rule isInfinite]) - repeat = @"DAILY"; - - [self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; - } - } - - /* WEEKLY */ - else if ([rule frequency] == iCalRecurrenceFrequenceWeekly) - { - repeatType = @"1"; - [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; - - if (![[rule byDay] length]) - { - if ([rule repeatInterval] == 1) - repeat = @"WEEKLY"; - else if ([rule repeatInterval] == 2) - repeat = @"BI-WEEKLY"; - } - else - { - [self setRepeat2: [[rule byDayMask] asRuleStringWithIntegers]]; - } - } - - /* MONTHLY */ - else if ([rule frequency] == iCalRecurrenceFrequenceMonthly) - { - repeatType = @"2"; - - if ([[rule byDay] length]) - { - int firstOccurrence; - iCalByDayMask *dayMask; - - dayMask = [rule byDayMask]; - firstOccurrence = [dayMask firstOccurrence] - 1; - if (firstOccurrence < 0) - firstOccurrence = 5; - - [self setRepeat2: @"0"]; - [self setRepeat3: [NSString stringWithFormat: @"%d", firstOccurrence]]; - [self setRepeat4: [NSString stringWithFormat: @"%d", [dayMask firstDay]]]; - } - else if ([[rule byMonthDay] count]) - { - NSArray *days; - - days = [rule byMonthDay]; - if ([days count] > 0 && [[days objectAtIndex: 0] intValue] < 0) - { - // BYMONTHDAY=-1 - [self setRepeat2: @"0"]; - [self setRepeat3: @"5"]; // last .. - [self setRepeat4: @"7"]; // .. day of the month - } - else - { - [self setRepeat2: @"1"]; - [self setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]]; - } - } - else if ([rule repeatInterval] == 1) - repeat = @"MONTHLY"; - - [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; - } - - /* YEARLY */ - else - { - repeatType = @"3"; - - if ([[rule flattenedValuesForKey: @"bymonth"] length]) - { - if ([[rule byDay] length]) - { - int firstOccurrence; - iCalByDayMask *dayMask; - - dayMask = [rule byDayMask]; - firstOccurrence = [dayMask firstOccurrence] - 1; - if (firstOccurrence < 0) - firstOccurrence = 5; - - [self setRepeat2: @"1"]; - [self setRepeat5: [NSString stringWithFormat: @"%d", firstOccurrence]]; - [self setRepeat6: [NSString stringWithFormat: @"%d", [dayMask firstDay]]]; - [self setRepeat7: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; - } - else - { - [self setRepeat2: @"0"]; - [self setRepeat3: [rule flattenedValuesForKey: @"bymonthday"]]; - [self setRepeat4: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; - } - } - else if ([rule repeatInterval] == 1) - repeat = @"YEARLY"; - - [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; - } - - /* We decode the proper end date, recurrences count, etc. */ - if ([rule repeatCount]) - { - repeat = @"CUSTOM"; - [self setRange1: @"1"]; - [self setRange2: [rule flattenedValuesForKey: @"count"]]; - } - else if ([rule untilDate]) - { - NSCalendarDate *date; - - repeat = @"CUSTOM"; - date = [[rule untilDate] copy]; - - ud = [[context activeUser] userDefaults]; - [date setTimeZone: [ud timeZone]]; - [self setRange1: @"2"]; - [self setRange2: [date descriptionWithCalendarFormat: dateFormat]]; - [date release]; - } - else - [self setRange1: @"0"]; - } - else - { - DESTROY(repeat); - repeatType = @"0"; - repeat1 = @"0"; - repeat2 = @"1"; - } -} - -- (void) _loadEMailAlarm: (iCalAlarm *) anAlarm -{ - NSArray *attendees; - iCalPerson *aAttendee; - SOGoUser *owner; - NSString *ownerId, *email; - int count, max; - - attendees = [anAlarm attendees]; - reminderEmailOrganizer = NO; - reminderEmailAttendees = NO; - - ownerId = [[self clientObject] ownerInContext: nil]; - owner = [SOGoUser userWithLogin: ownerId]; - email = [[owner defaultIdentity] objectForKey: @"email"]; - - max = [attendees count]; - for (count = 0; - !(reminderEmailOrganizer && reminderEmailAttendees) - && count < max; - count++) - { - aAttendee = [attendees objectAtIndex: count]; - if ([[aAttendee rfc822Email] isEqualToString: email]) - reminderEmailOrganizer = YES; - else - reminderEmailAttendees = YES; - } -} - -- (void) _loadAlarms -{ - iCalAlarm *anAlarm; - iCalTrigger *aTrigger; - NSString *duration, *quantity; - unichar c; - NSUInteger i; - - if ([component hasAlarms]) - { - // We currently have the following limitations for alarms: - // - the alarm's action must be of type DISPLAY or AUDIO (considered as DISPLAY) - // - the alarm's trigger value type must be DURATION. - - anAlarm = [component firstSupportedAlarm]; - aTrigger = [anAlarm trigger]; - ASSIGN (reminderAction, [[anAlarm action] lowercaseString]); - - // The default value type is DURATION. See http://tools.ietf.org/html/rfc5545#section-3.8.6.3 - if (![[aTrigger valueType] length] || - [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame) - { - duration = [aTrigger flattenedValuesForKey: @""]; - i = [reminderValues indexOfObject: duration]; - - if (i == NSNotFound || [reminderAction isEqualToString: @"email"]) - { - // Custom alarm - ASSIGN (reminder, @"CUSTOM"); - ASSIGN (reminderRelation, [aTrigger relationType]); - - i = 0; - c = [duration characterAtIndex: i]; - if (c == '-') - { - ASSIGN (reminderReference, @"BEFORE"); - i++; - } - else - { - ASSIGN (reminderReference, @"AFTER"); - } - - c = [duration characterAtIndex: i]; - if (c == 'P') - { - quantity = @""; - // Parse duration -- ignore first character (P) - for (i++; i < [duration length]; i++) - { - c = [duration characterAtIndex: i]; - if (c == 't' || c == 'T') - // time -- ignore character - continue; - else if (isdigit (c)) - quantity = [quantity stringByAppendingFormat: @"%c", c]; - else - { - switch (c) - { - case 'D': /* day */ - ASSIGN (reminderUnit, @"DAYS"); - break; - case 'H': /* hour */ - ASSIGN (reminderUnit, @"HOURS"); - break; - case 'M': /* min */ - ASSIGN (reminderUnit, @"MINUTES"); - break; - default: - //NSLog(@"Cannot process duration unit: '%c'", c); - break; - } - } - } - if ([quantity length]) - ASSIGN (reminderQuantity, quantity); - - if ([reminderAction isEqualToString: @"email"]) - [self _loadEMailAlarm: anAlarm]; - } - } - else - // Matches one of the predefined alarms - ASSIGN (reminder, [reminderItems objectAtIndex: i]); - } - } -} - -/* warning: we use this method which will be triggered by the template system - when the page is instantiated, but we should find another and cleaner way of - doing this... for example, when the clientObject is set */ -- (void) setComponent: (iCalRepeatableEntityObject *) newComponent -{ - SOGoCalendarComponent *co; - SOGoUserManager *um; - NSString *owner, *ownerEmail; - iCalRepeatableEntityObject *masterComponent; - SOGoUserDefaults *defaults; - NSString *tag; - - if (!component) - { - ASSIGN (component, newComponent); - - co = [self clientObject]; - componentOwner = [co ownerInContext: nil]; - if (component) - { - ASSIGN (title, [component summary]); - ASSIGN (location, [component location]); - ASSIGN (comment, [component comment]); - ASSIGN (attachUrl, [[component attach] absoluteString]); - ASSIGN (classification, [component accessClass]); - if ([co isNew] && [classification length] == 0) - { - defaults = [[context activeUser] userDefaults]; - tag = [co componentTag]; - [classification release]; - if ([tag isEqualToString: @"vevent"]) - classification = [defaults calendarEventsDefaultClassification]; - else - classification = [defaults calendarTasksDefaultClassification]; - - if ([classification length] == 0) - classification = @"PUBLIC"; - [classification retain]; - } - - ASSIGN (priority, [component priority]); - ASSIGN (status, [component status]); - ASSIGN (categories, [component categories]); - if ([[[component organizer] rfc822Email] length]) - { - ASSIGN (organizer, [component organizer]); - } - else - { - masterComponent = [[[component parent] allObjects] objectAtIndex: 0]; - ASSIGN (organizer, [masterComponent organizer]); - } - - [self _loadCategories]; - if (!jsonAttendees) - [self _loadAttendees]; - [self _loadRRules]; - [self _loadAlarms]; - - [componentCalendar release]; - componentCalendar = [co container]; - if ([componentCalendar isKindOfClass: [SOGoCalendarComponent class]]) - componentCalendar = [componentCalendar container]; - [componentCalendar retain]; - - um = [SOGoUserManager sharedUserManager]; - owner = [componentCalendar ownerInContext: context]; - ownerEmail = [um getEmailForUID: owner]; - ASSIGN (ownerAsAttendee, [component findAttendeeWithEmail: (id)ownerEmail]); - } - } -} - -- (void) setRSVPURL: (NSString *) theURL -{ - rsvpURL = theURL; -} - -- (NSString *) rsvpURL -{ - return rsvpURL; -} - -- (void) setSaveURL: (NSString *) newSaveURL -{ - saveURL = newSaveURL; -} - -- (NSString *) saveURL -{ - return saveURL; -} - -/* accessors */ - -- (BOOL) isChildOccurence -{ - return [[self clientObject] isKindOfClass: [SOGoComponentOccurence class]]; -} - -- (void) setItem: (id) _item -{ - ASSIGN (item, _item); -} - -- (id) item -{ - return item; -} - -- (NSString *) itemPriorityText -{ - return [self labelForKey: [NSString stringWithFormat: @"prio_%@", item]]; -} - -- (NSString *) itemClassificationText -{ - NSString *tag; - - tag = [[component tag] lowercaseString]; - - return [self labelForKey: [NSString stringWithFormat: @"%@_%@", item, tag]]; -} - -- (NSString *) itemStatusText -{ - return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; -} - -- (void) setTitle: (NSString *) _value -{ - ASSIGN (title, _value); -} - -- (NSString *) title -{ - SOGoCalendarComponent *co; - NSString *tag; - - co = [self clientObject]; - if ([co isNew] && [co isKindOfClass: [SOGoCalendarComponent class]]) - { - tag = [co componentTag]; - if ([tag isEqualToString: @"vevent"]) - [self setTitle: [self labelForKey: @"New Event"]]; - else if ([tag isEqualToString: @"vtodo"]) - [self setTitle: [self labelForKey: @"New Task"]]; - } - - return title; -} - -- (void) setAttach: (NSString *) _attachUrl -{ - ASSIGN (attachUrl, _attachUrl); -} - -- (NSString *) attach -{ - return attachUrl; -} - -- (NSDictionary *) organizerProfile -{ - NSMutableDictionary *profile; - NSDictionary *ownerIdentity; - NSString *uid, *name, *email, *partstat, *role; - SOGoUserManager *um; - SOGoCalendarComponent *co; - SOGoUser *ownerUser; - - if (organizerProfile == nil) - { - profile = [NSMutableDictionary dictionary]; - email = [organizer rfc822Email]; - role = nil; - partstat = nil; - - if ([email length]) - { - um = [SOGoUserManager sharedUserManager]; - - name = [organizer cn]; - uid = [um getUIDForEmail: email]; - - partstat = [[organizer partStat] lowercaseString]; - role = [[organizer role] lowercaseString]; - } - else - { - // No organizer defined in vEvent; use calendar owner - co = [self clientObject]; - uid = [[co container] ownerInContext: context]; - ownerUser = [SOGoUser userWithLogin: uid roles: nil]; - ownerIdentity = [ownerUser defaultIdentity]; - - name = [ownerIdentity objectForKey: @"fullName"]; - email = [ownerIdentity objectForKey: @"email"]; - } - - if (uid != nil) - [profile setObject: uid - forKey: @"uid"]; - else - uid = email; - - [profile setObject: name - forKey: @"name"]; - - [profile setObject: email - forKey: @"email"]; - - if (partstat == nil || ![partstat length]) - partstat = @"accepted"; - - [profile setObject: partstat - forKey: @"partstat"]; - - if (role == nil || ![role length]) - role = @"chair"; - - [profile setObject: role - forKey: @"role"]; - - organizerProfile = [NSDictionary dictionaryWithObject: profile forKey: uid]; - [organizerProfile retain]; - } +// sm = [SoSecurityManager sharedSecurityManager]; - return organizerProfile; -} - - -- (BOOL) hasCreatedBy -{ - return ([[[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""] length] > 0); -} - -- (NSString *) createdBy -{ - return [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]; -} - -- (NSString *) createdByLink -{ - return [NSString stringWithFormat: @"mailto:%@", - [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]]; -} - -- (NSString *) createdByName -{ - NSString *login; - SOGoUser *user; - - login = [[SOGoUserManager sharedUserManager] getUIDForEmail: [self createdBy]]; - - if (login) - { - user = [SOGoUser userWithLogin: login]; - - if (user) - return [user cn]; - } - - return @""; -} - -- (NSString *) organizerName -{ - NSDictionary *profile; - NSString *s; - - profile = [[[self organizerProfile] allValues] lastObject]; - - s = [profile objectForKey: @"name"]; - - if ([s length] == 0) - s = [profile objectForKey: @"email"]; - - return s; -} - -- (NSString *) jsonOrganizer -{ - return [[[[self organizerProfile] allValues] lastObject] jsonRepresentation]; -} - -- (BOOL) hasOrganizer -{ - // We check if there's an organizer and if it's not ourself - NSString *value; - - value = [organizer rfc822Email]; - - if ([value length]) - { - NSDictionary *currentIdentity; - NSEnumerator *identities; - NSArray *allIdentities; - - allIdentities = [[context activeUser] allIdentities]; - identities = [allIdentities objectEnumerator]; - currentIdentity = nil; - - while ((currentIdentity = [identities nextObject])) - if ([[currentIdentity objectForKey: @"email"] - caseInsensitiveCompare: value] - == NSOrderedSame) - return NO; - - return YES; - } - - return NO; -} - -- (BOOL) hasAttendees -{ - return ([[component attendees] count] > 0); -} - -- (void) setAttendee: (id) _attendee -{ - ASSIGN (attendee, _attendee); -} - -- (id) attendee -{ - return attendee; -} - -- (NSString *) attendeeForDisplay -{ - NSString *fn, *result; - - fn = [attendee cnWithoutQuotes]; - if ([fn length]) - result = fn; - else - result = [attendee rfc822Email]; - - return result; -} - -- (NSString *) jsonAttendees -{ - return [jsonAttendees jsonRepresentation]; -} - -- (void) setLocation: (NSString *) _value -{ - ASSIGN (location, _value); -} - -- (NSString *) location -{ - return location; -} - -- (BOOL) hasLocation -{ - return [location length] > 0; -} - -- (void) setComment: (NSString *) _value -{ -#warning should we do the same for "location" and "summary"? What about ContactsUI? - ASSIGN (comment, [_value stringByReplacingString: @"\r\n" withString: @"\n"]); -} - -- (NSString *) comment -{ - return [comment stringByReplacingString: @"\n" withString: @"\r\n"]; -} - -- (BOOL) hasComment -{ - return [comment length] > 0; -} - -- (NSArray *) categoryList -{ - NSMutableArray *categoryList; - NSArray *categoryLabels; - SOGoUserDefaults *defaults; - - defaults = [[context activeUser] userDefaults]; - categoryLabels = [defaults calendarCategories]; - if (!categoryLabels) - categoryLabels = [[self labelForKey: @"category_labels"] - componentsSeparatedByString: @","]; - categoryList - = [NSMutableArray arrayWithCapacity: [categoryLabels count] + 1]; - if ([category length] && ![categoryLabels containsObject: category]) - [categoryList addObject: category]; - [categoryList addObjectsFromArray: - [categoryLabels sortedArrayUsingSelector: - @selector (localizedCaseInsensitiveCompare:)]]; - - return categoryList; -} - -- (void) setCategories: (NSArray *) _categories -{ - ASSIGN (categories, _categories); -} - -- (NSArray *) categories -{ - return categories; -} - -- (void) setCategory: (NSString *) newCategory -{ - if (newCategory) - ASSIGN (categories, [NSArray arrayWithObject: newCategory]); - else - { - [categories release]; - categories = nil; - } -} - -- (NSString *) category -{ - return category; -} - -- (BOOL) hasCategory -{ - return [category length] > 0; -} - -- (NSArray *) repeatList -{ - static NSArray *repeatItems = nil; - - if (!repeatItems) - { - repeatItems = [NSArray arrayWithObjects: @"DAILY", - @"WEEKLY", - @"BI-WEEKLY", - @"EVERY WEEKDAY", - @"MONTHLY", - @"YEARLY", - @"-", - @"CUSTOM", - nil]; - [repeatItems retain]; - } - - return repeatItems; -} - -- (NSString *) itemRepeatText -{ - NSString *text; - - if ([item isEqualToString: @"-"]) - text = item; - else - text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]]; - - return text; -} - -- (NSString *) repeatLabel -{ - NSString *rc; - - if ([self repeat]) - rc = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", [self repeat]]]; - else - rc = [self labelForKey: @"repeat_NEVER"]; - - return rc; -} - -- (NSArray *) reminderList -{ - return reminderItems; -} - -- (void) setReminder: (NSString *) theReminder -{ - ASSIGN(reminder, theReminder); -} - -- (NSString *) reminder -{ - if ([[self clientObject] isNew]) - { - NSString *value; - NSUInteger index; - - value = [userDefaults calendarDefaultReminder]; - index = [reminderValues indexOfObject: value]; - - if (index != NSNotFound) - return [reminderItems objectAtIndex: index]; - - return @"NONE"; - } - - return reminder; -} - -- (void) setReminderQuantity: (NSString *) theReminderQuantity -{ - ASSIGN(reminderQuantity, theReminderQuantity); -} - -- (NSString *) reminderQuantity -{ - return reminderQuantity; -} - -- (NSString *) itemReminderText -{ - NSString *text; - - if ([item isEqualToString: @"-"]) - text = item; - else - text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]]; - - return text; -} - -- (void) setReminderAction: (NSString *) newValue -{ - ASSIGN (reminderAction, newValue); -} - -- (NSString *) reminderAction -{ - return reminderAction; -} - -- (void) setReminderEmailOrganizer: (NSString *) newValue -{ - reminderEmailOrganizer = [newValue isEqualToString: @"true"]; -} - -- (NSString *) reminderEmailOrganizer -{ - return (reminderEmailOrganizer ? @"true" : @"false"); -} - -- (void) setReminderEmailAttendees: (NSString *) newValue -{ - reminderEmailAttendees = [newValue isEqualToString: @"true"]; -} - -- (NSString *) reminderEmailAttendees -{ - return (reminderEmailAttendees ? @"true" : @"false"); -} - -- (NSString *) repeat -{ - return repeat; -} - -- (void) setRepeat: (NSString *) newRepeat -{ - ASSIGN(repeat, newRepeat); -} - -- (BOOL) hasRepeat -{ - return [repeat length] > 0; -} - -- (NSString *) itemReplyText -{ - NSString *word; - - 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]]; -} - -- (NSArray *) replyList -{ - return [NSArray arrayWithObjects: - [NSNumber numberWithInt: iCalPersonPartStatAccepted], - [NSNumber numberWithInt: iCalPersonPartStatDeclined], - [NSNumber numberWithInt: iCalPersonPartStatNeedsAction], - [NSNumber numberWithInt: iCalPersonPartStatTentative], - [NSNumber numberWithInt: iCalPersonPartStatDelegated], - nil]; -} - -- (NSNumber *) reply -{ - iCalPersonPartStat participationStatus; - - participationStatus = [ownerAsAttendee participationStatus]; - - return [NSNumber numberWithInt: participationStatus]; -} - -- (NSArray *) calendarList -{ - SOGoAppointmentFolder *calendar, *currentCalendar; - SOGoAppointmentFolders *calendarParent; - NSEnumerator *allCalendars; - SoSecurityManager *sm; - NSString *perm; - - if (!calendarList) - { - calendarList = [NSMutableArray new]; - calendar = [self componentCalendar]; - sm = [SoSecurityManager sharedSecurityManager]; - - perm = SoPerm_DeleteObjects; - if ([sm validatePermission: perm - onObject: calendar - inContext: context]) - { - // User can't delete components from this calendar; - // don't add any calendar other than the current one - [calendarList addObject: calendar]; - } - else - { - // Find which calendars user has creation rights - perm = SoPerm_AddDocumentsImagesAndFiles; - calendarParent - = [[context activeUser] calendarsFolderInContext: context]; - allCalendars = [[calendarParent subFolders] objectEnumerator]; - while ((currentCalendar = [allCalendars nextObject])) - if ([calendar isEqual: currentCalendar] || - ![sm validatePermission: perm - onObject: currentCalendar - inContext: context]) - [calendarList addObject: currentCalendar]; - } - } - - return calendarList; -} - -/** - * This method is called from the wox template and uses to display the event - * organizer in the edition window of the attendees. - * Returns an array of the two elements : - * - array of calendar owners - * - dictionary of owners profiles - */ -- (NSArray *) calendarOwnerList -{ - NSArray *calendars; - NSMutableArray *owners; - NSDictionary *currentOwnerIdentity; - NSMutableDictionary *profiles, *currentOwnerProfile; - NSString *currentOwner; - SOGoAppointmentFolder *currentCalendar; - SOGoUser *currentUser; - NSUInteger i; - - calendars = [self calendarList]; - owners = [NSMutableArray arrayWithCapacity: [calendars count]]; - profiles = [NSMutableDictionary dictionaryWithDictionary: [self organizerProfile]]; - - for (i = 0; i < [calendars count]; i++) - { - currentCalendar = [calendars objectAtIndex: i]; - currentOwner = [currentCalendar ownerInContext: context]; - [owners addObject: currentOwner]; - - if ([profiles objectForKey: currentOwner] == nil) - { - currentUser = [SOGoUser userWithLogin: currentOwner roles: nil]; - currentOwnerIdentity = [currentUser defaultIdentity]; - - currentOwnerProfile = [NSMutableDictionary dictionary]; - [currentOwnerProfile setObject: ([currentOwnerIdentity objectForKey: @"fullName"] == nil ? @"" : [currentOwnerIdentity objectForKey: @"fullName"]) - forKey: @"name"]; - [currentOwnerProfile setObject: ([currentOwnerIdentity objectForKey: @"email"] == nil ? @"" : [currentOwnerIdentity objectForKey: @"email"]) - forKey: @"email"]; - [currentOwnerProfile setObject: @"accepted" - forKey: @"partstat"]; - [currentOwnerProfile setObject: @"chair" - forKey: @"role"]; - - [profiles setObject: currentOwnerProfile forKey: currentOwner]; - } - } - - return [NSArray arrayWithObjects: owners, profiles, nil]; -} - -- (NSString *) calendarDisplayName -{ - NSString *fDisplayName; - SOGoAppointmentFolder *folder; - SOGoAppointmentFolders *parentFolder; - - fDisplayName = [item displayName]; - folder = [self componentCalendar]; - parentFolder = [folder container]; - if ([fDisplayName isEqualToString: [parentFolder defaultFolderName]]) - fDisplayName = [self labelForKey: fDisplayName]; - - return fDisplayName; -} - -- (NSString *) calendarsFoldersList -{ - NSArray *calendars; - - calendars = [[self calendarList] valueForKey: @"nameInContainer"]; - - return [calendars componentsJoinedByString: @","]; -} - - -- (SOGoAppointmentFolder *) componentCalendar -{ - return componentCalendar; -} - -- (NSString *) componentCalendarName -{ - return [componentCalendar displayName]; -} - -- (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar -{ - if (_componentCalendar) - ASSIGN(componentCalendar, _componentCalendar); -} - -/* priorities */ - -- (NSArray *) priorities -{ - /* 0 == undefined - 9 == low - 5 == medium - 1 == high - */ - static NSArray *priorities = nil; - - if (!priorities) - { - priorities = [NSArray arrayWithObjects: @"9", @"5", @"1", nil]; - [priorities retain]; - } - - return priorities; -} - -- (void) setPriority: (NSString *) _priority -{ - ASSIGN (priority, _priority); -} - -- (NSString *) priority -{ - return priority; -} - -- (BOOL) hasPriority -{ - return [priority length] > 0; -} - -- (NSArray *) classificationClasses -{ - static NSArray *classes = nil; - - if (!classes) - { - classes = [NSArray arrayWithObjects: @"PUBLIC", - @"CONFIDENTIAL", @"PRIVATE", nil]; - [classes retain]; - } - - return classes; -} - -- (void) setClassification: (NSString *) _classification -{ - ASSIGN (classification, _classification); -} - -- (NSString *) classification -{ - return classification; -} - -- (void) setStatus: (NSString *) _status -{ - ASSIGN (status, _status); -} - -- (NSString *) status -{ - return status; -} - -- (void) setRepeatType: (NSString *) theValue -{ - ASSIGN (repeatType, theValue); -} - -- (NSString *) repeatType -{ - return repeatType; -} - -REPEAT(1); -REPEAT(2); -REPEAT(3); -REPEAT(4); -REPEAT(5); -REPEAT(6); -REPEAT(7); -RANGE(1); -RANGE(2); - -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -- (NSArray *) cycles -{ - NSString *path; - static NSArray *cycles = nil; - - if (!cycles) - { - path = [[self componentBundle] pathForResource: @"cycles" ofType: @"plist"]; - NSAssert(path != nil, @"Cannot find cycles.plist!"); - cycles = [[NSArray arrayWithContentsOfFile:path] retain]; - NSAssert(cycles != nil, @"Cannot instantiate cycles from cycles.plist!"); - } - - return cycles; -} - -- (void) setCycle: (NSDictionary *) _cycle -{ - ASSIGN (cycle, _cycle); -} - -- (NSDictionary *) cycle -{ - return cycle; -} - -- (BOOL) hasCycle -{ - return ([cycle objectForKey: @"rule"] != nil); -} - -- (NSString *) cycleLabel -{ - NSString *key; - - key = [(NSDictionary *)item objectForKey: @"label"]; - - return [self labelForKey:key]; -} - -- (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate -{ -// NSCalendarDate *until; - -// /* copy hour/minute/second from startDate */ -// until = [_cycleUntilDate hour: [startDate hourOfDay] -// minute: [startDate minuteOfHour] -// second: [startDate secondOfMinute]]; -// [until setTimeZone: [startDate timeZone]]; -// ASSIGN (cycleUntilDate, until); -} - -- (NSCalendarDate *) cycleUntilDate -{ - return cycleUntilDate; -} - -- (iCalRecurrenceRule *) rrule -{ - NSString *ruleRep; - iCalRecurrenceRule *rule; - - if (![self hasCycle]) - return nil; - ruleRep = [cycle objectForKey: @"rule"]; - rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; - - if (cycleUntilDate && [self isCycleEndUntil]) - [rule setUntilDate:cycleUntilDate]; - - return rule; -} - -- (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule -{ -// NSDictionary *c; -// NSCalendarDate *until; - -// c = [self cycleMatchingRRule:_rrule]; -// [self setCycle:c]; - -// until = [[[_rrule untilDate] copy] autorelease]; -// if (!until) -// until = startDate; -// else -// [self setIsCycleEndUntil]; - -// [until setTimeZone:[[self clientObject] userTimeZone]]; -// [self setCycleUntilDate:until]; -} - -/* - This method is necessary, because we have a fixed sets of cycles in the UI. - The model is able to represent arbitrary rules, however. - There SHOULD be a different UI, similar to iCal.app, to allow modelling - of more complex rules. - - This method obviously cannot map all existing rules back to the fixed list - in cycles.plist. This should be fixed in a future version when interop - becomes more important. - */ -- (NSDictionary *) cycleMatchingRRule: (iCalRecurrenceRule *) _rrule -{ - NSString *cycleRep; - NSArray *cycles; - NSUInteger i, count; - - if (!_rrule) - return [[self cycles] objectAtIndex:0]; - - cycleRep = [_rrule versitString]; - cycles = [self cycles]; - count = [cycles count]; - for (i = 1; i < count; i++) { - NSDictionary *c; - NSString *cr; - - c = [cycles objectAtIndex:i]; - cr = [c objectForKey: @"rule"]; - if ([cr isEqualToString:cycleRep]) - return c; - } - [self warnWithFormat: @"No default cycle for rrule found! -> %@", _rrule]; - return nil; -} - -/* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */ -- (NSArray *) cycleEnds -{ - static NSArray *ends = nil; - - if (!ends) - { - ends = [NSArray arrayWithObjects: @"cycle_end_never", - @"cycle_end_until", nil]; - [ends retain]; - } - - return ends; -} - -- (void) setCycleEnd: (NSString *) _cycleEnd -{ - ASSIGN (cycleEnd, _cycleEnd); -} - -- (NSString *) cycleEnd -{ - return cycleEnd; -} - -- (BOOL) isCycleEndUntil -{ - return (cycleEnd && [cycleEnd isEqualToString: @"cycle_end_until"]); -} - -- (void) setIsCycleEndUntil -{ - [self setCycleEnd: @"cycle_end_until"]; -} - -- (void) setIsCycleEndNever -{ - [self setCycleEnd: @"cycle_end_never"]; -} -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// -////////////////////////////////// JUNK //////////////////////////////////////// - +// return ([co isNew] +// || (([owner isEqualToString: [currentUser login]] +// || ![sm validatePermission: SOGoCalendarPerm_ModifyComponent +// onObject: co +// inContext: context]) +// && (!hasOrganizer || [component userIsOrganizer: currentUser]))); +// } + +//- (void) setOrganizerIdentity: (NSDictionary *) newOrganizerIdentity +//{ +// ASSIGN (organizerIdentity, newOrganizerIdentity); +//} + +// - (NSDictionary *) organizerIdentity +// { +// NSArray *allIdentities; +// NSEnumerator *identities; +// NSDictionary *currentIdentity; +// NSString *orgEmail; + +// orgEmail = [organizer rfc822Email]; +// if (!organizerIdentity) +// { +// if ([orgEmail length]) +// { +// allIdentities = [[context activeUser] allIdentities]; +// identities = [allIdentities objectEnumerator]; +// while (!organizerIdentity +// && ((currentIdentity = [identities nextObject]))) +// if ([[currentIdentity objectForKey: @"email"] +// caseInsensitiveCompare: orgEmail] +// == NSOrderedSame) +// ASSIGN (organizerIdentity, currentIdentity); +// } +// } + +// return organizerIdentity; +// } + +//- (void) setComment: (NSString *) _value +//{ +//#warning should we do the same for "location" and "summary"? What about ContactsUI? +// ASSIGN (comment, [_value stringByReplacingString: @"\r\n" withString: @"\n"]); +//} +// +//- (NSString *) comment +//{ +// return [comment stringByReplacingString: @"\n" withString: @"\r\n"]; +//} + +// TODO: Expose this method to the JSON API or centralize in UIxPreferences +// - (NSArray *) categoryList +// { +// NSMutableArray *categoryList; +// NSArray *categoryLabels; +// SOGoUserDefaults *defaults; + +// defaults = [[context activeUser] userDefaults]; +// categoryLabels = [defaults calendarCategories]; +// if (!categoryLabels) +// categoryLabels = [[self labelForKey: @"category_labels"] +// componentsSeparatedByString: @","]; +// categoryList = [NSMutableArray arrayWithCapacity: [categoryLabels count] + 1]; +// if ([category length] && ![categoryLabels containsObject: category]) +// [categoryList addObject: category]; +// [categoryList addObjectsFromArray: +// [categoryLabels sortedArrayUsingSelector: +// @selector (localizedCaseInsensitiveCompare:)]]; + +// return categoryList; +// } + +//- (NSArray *) repeatList +//{ +// static NSArray *repeatItems = nil; +// +// if (!repeatItems) +// { +// repeatItems = [NSArray arrayWithObjects: @"DAILY", +// @"WEEKLY", +// @"BI-WEEKLY", +// @"EVERY WEEKDAY", +// @"MONTHLY", +// @"YEARLY", +// @"-", +// @"CUSTOM", +// nil]; +// [repeatItems retain]; +// } +// +// return repeatItems; +//} + +//- (NSString *) repeatLabel +//{ +// NSString *rc; +// +// if ([self repeat]) +// rc = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", [self repeat]]]; +// else +// rc = [self labelForKey: @"repeat_NEVER"]; +// +// return rc; +//} + +//- (NSString *) reminder +//{ +// if ([[self clientObject] isNew]) +// { +// NSString *value; +// NSUInteger index; +// +// value = [userDefaults calendarDefaultReminder]; +// index = [reminderValues indexOfObject: value]; +// +// if (index != NSNotFound) +// return [reminderItems objectAtIndex: index]; +// +// return @"NONE"; +// } +// +// return reminder; +//} + +//- (NSString *) itemReminderText +//{ +// NSString *text; +// +// if ([item isEqualToString: @"-"]) +// text = item; +// else +// text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]]; +// +// return text; +//} + +//- (NSString *) itemReplyText +//{ +// NSString *word; +// +// 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]]; +//} + +//- (NSArray *) replyList +//{ +// return [NSArray arrayWithObjects: +// [NSNumber numberWithInt: iCalPersonPartStatAccepted], +// [NSNumber numberWithInt: iCalPersonPartStatDeclined], +// [NSNumber numberWithInt: iCalPersonPartStatNeedsAction], +// [NSNumber numberWithInt: iCalPersonPartStatTentative], +// [NSNumber numberWithInt: iCalPersonPartStatDelegated], +// nil]; +//} +// +//- (NSNumber *) reply +//{ +// iCalPersonPartStat participationStatus; +// +// participationStatus = [ownerAsAttendee participationStatus]; +// +// return [NSNumber numberWithInt: participationStatus]; +//} + +///* priorities */ +// +//- (NSArray *) priorities +//{ +// /* 0 == undefined +// 9 == low +// 5 == medium +// 1 == high +// */ +// static NSArray *priorities = nil; +// +// if (!priorities) +// { +// priorities = [NSArray arrayWithObjects: @"9", @"5", @"1", nil]; +// [priorities retain]; +// } +// +// return priorities; +//} + +//- (NSArray *) classificationClasses +//{ +// static NSArray *classes = nil; +// +// if (!classes) +// { +// classes = [NSArray arrayWithObjects: @"PUBLIC", +// @"CONFIDENTIAL", @"PRIVATE", nil]; +// [classes retain]; +// } +// +// return classes; +//} /* helpers */ -- (NSString *) completeURIForMethod: (NSString *) _method -{ - NSString *uri; - NSRange r; - - uri = [[[self context] request] uri]; - - /* first: identify query parameters */ - r = [uri rangeOfString: @"?" options:NSBackwardsSearch]; - if (r.length > 0) - uri = [uri substringToIndex:r.location]; - - /* next: append trailing slash */ - if (![uri hasSuffix: @"/"]) - uri = [uri stringByAppendingString: @"/"]; - - /* next: append method */ - uri = [uri stringByAppendingString:_method]; - - /* next: append query parameters */ - return [self completeHrefForMethod:uri]; -} -- (BOOL) isWriteableClientObject -{ - return [[self clientObject] - respondsToSelector: @selector(saveCompontent:)]; -} - -/* access */ - -- (BOOL) canEditComponent -{ - return ([[context activeUser] hasEmail: [organizer rfc822Email]]); -} - -/* response generation */ - -- (NSString *) initialCycleVisibility -{ - return ([self hasCycle] - ? @"visibility: visible;" - : @"visibility: hidden;"); -} - -- (NSString *) initialCycleEndUntilVisibility { - return ([self isCycleEndUntil] - ? @"visibility: visible;" - : @"visibility: hidden;"); -} +//- (BOOL) isWriteableClientObject +//{ +// return [[self clientObject] +// respondsToSelector: @selector(saveCompontent:)]; +//} +// +///* access */ +// +//- (BOOL) canEditComponent +//{ +// return ([[context activeUser] hasEmail: [organizer rfc822Email]]); +//} +// +///* response generation */ +// // - (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters // { @@ -1731,19 +480,6 @@ RANGE(2); // return iCalRep; // } -- (NSException *) validateObjectForStatusChange -{ - id co; - - co = [self clientObject]; - if (![co respondsToSelector: @selector(changeParticipationStatus:)]) - return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ - reason: - @"method cannot be invoked on the specified object"]; - - return nil; -} - /* contact editor compatibility */ /*- (NSString *) urlButtonClasses @@ -1756,7 +492,7 @@ RANGE(2); classes = @"button _disabled"; return classes; - }*/ +}*/ - (void) _handleAttendeesEdition { @@ -1829,21 +565,21 @@ RANGE(2); - (void) _handleOrganizer { NSString *owner, *login, *currentEmail; - BOOL isOwner, hasAttendees; + iCalPerson *organizer; + BOOL isOwner; //owner = [[self clientObject] ownerInContext: context]; owner = [componentCalendar ownerInContext: context]; login = [[context activeUser] login]; isOwner = [owner isEqualToString: login]; - hasAttendees = [self hasAttendees]; currentEmail = [[[context activeUser] allEmails] objectAtIndex: 0]; - if (hasAttendees) + if ([[component attendees] count] > 0) { SOGoUser *user; id identity; - ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); + organizer = [iCalPerson elementWithTag: @"organizer"]; [component setOrganizer: organizer]; user = [SOGoUser userWithLogin: owner roles: nil]; @@ -1852,14 +588,14 @@ RANGE(2); [organizer setEmail: [identity objectForKey: @"email"]]; if (!isOwner) - { - NSString *quotedEmail; + { + NSString *quotedEmail; quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]; [organizer setValue: 0 ofAttribute: @"SENT-BY" to: quotedEmail]; - } + } } else { @@ -1874,594 +610,224 @@ RANGE(2); [currentEmail length]) { [component addChild: [CardElement simpleElementWithTag: @"X-SOGo-Component-Created-By" - value: currentEmail]]; - } - -} - -- (void) _handleCustomRRule: (iCalRecurrenceRule *) theRule - -{ - int type, range; - NSMutableArray *values; - - // We decode the range - range = [[self range1] intValue]; - - // Create X appointments - if (range == 1) - { - [theRule setRepeatCount: [[self range2] intValue]]; - } - // Repeat until date - else if (range == 2) - { - NSCalendarDate *date; - SOGoUserDefaults *ud; - - date = [NSCalendarDate dateWithString: [self range2] - calendarFormat: dateFormat - locale: locale]; - - // Adjust timezone - ud = [[context activeUser] userDefaults]; - date = [NSCalendarDate dateWithYear: [date yearOfCommonEra] - month: [date monthOfYear] - day: [date dayOfMonth] - hour: 0 minute: 0 second: 0 - timeZone: [ud timeZone]]; - - [theRule setUntilDate: date]; - } - // No end date. - else - { - // Do nothing? - } - - - // We decode the type and the rest accordingly. - type = [[self repeatType] intValue]; - - switch (type) - { - // DAILY (0) - // - // repeat1 holds the value of the frequency radio button: - // 0 -> Every X days - // 1 -> Every weekday - // - // repeat2 holds the value of X when repeat1 equals 0 - // - case 0: - { - [theRule setFrequency: iCalRecurrenceFrequenceDaily]; - - if ([[self repeat1] intValue] > 0) - { - [theRule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]]; - } - else - { - // Make sure we haven't received any junk.... - if ([[self repeat2] intValue] < 1) - [self setRepeat2: @"1"]; - - [theRule setInterval: [self repeat2]]; - } - } - break; - - // WEEKLY (1) - // - // repeat1 holds the value of "Every X week(s)" - // - // repeat2 holds which days are part of the recurrence rule - // 1 -> Monday - // 2 -> Tuesday .. and so on. - // The list is separated by commas, like: 1,3,4 - case 1: - { - if ([[self repeat1] intValue] > 0) - { - NSArray *v; - int c, day; - iCalWeekOccurrences days; - - [theRule setFrequency: iCalRecurrenceFrequenceWeekly]; - [theRule setInterval: [self repeat1]]; - - if ([[self repeat2] length]) - { - v = [[self repeat2] componentsSeparatedByString: @","]; - c = [v count]; - memset(days, 0, 7 * sizeof(iCalWeekOccurrence)); - - while (c--) - { - day = [[v objectAtIndex: c] intValue]; - if (day >= 0 && day <= 7) - days[day] = iCalWeekOccurrenceAll; - } - [theRule setByDayMask: [iCalByDayMask byDayMaskWithDays: days]]; - } - } - } - break; - - // MONTHLY (2) - // - // repeat1 holds the value of "Every X month(s)" - // - // repeat2 holds the value of the radio-button "The" / "Recur on day(s)" - // 0 -> The - // 1 -> Recur on day(s) - // - // repeat3 holds the value of the first popup - // 0 -> First - // 1 -> Second ... and so on. - // - // repeat4 holds the value of the second popop - // 0 -> Sunday - // 1 -> Monday ... and so on. - // 7 -> Day of the month - // - // repeat5 holds the selected days when "Recur on day(s)" - // is chosen. The value starts at 1. - // - case 2: - { - if ([[self repeat1] intValue] > 0) - { - [theRule setFrequency: iCalRecurrenceFrequenceMonthly]; - [theRule setInterval: [self repeat1]]; - - // We recur on specific days... - if ([[self repeat2] intValue] == 0) - { - NSString *day; - int occurence; - - day = [theRule iCalRepresentationForWeekDay: [[self repeat4] intValue]]; - occurence = [[self repeat3] intValue] + 1; - if (occurence > 5) // the first/second/third/fourth/fifth .. - occurence = -1; // the last .. - [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", - occurence, day] - forKey: @"byday"]; - } - else - { - if ([[self repeat5] intValue] > 0) - { - values = [[[self repeat5] - componentsSeparatedByString: @","] - mutableCopy]; - [theRule setValues: values - atIndex: 0 forKey: @"bymonthday"]; - [values release]; - } - } - } - } - break; - - // YEARLY (3) - // - // repeat1 holds the value of "Every X year(s)" - // - // repeat2 holds the value of the radio-button "Every" / "Every .. of .." - // 0 -> Every - // 1 -> Every .. of .. - // - // repeat3 holds the value of the DAY parameter - // repeat4 holds the value of the MONTH parameter (0 -> January, 1 -> February ... ) - // ex: 3 February - // - // repeat5 holds the value of the OCCURENCE parameter (0 -> First, 1 -> Second .., 5 -> Last) - // repeat6 holds the value of the DAY parameter (0 -> Sunday, 1 -> Monday, etc..) - // repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... ) - // - case 3: - default: - { - if ([[self repeat1] intValue] > 0) - { - [theRule setFrequency: iCalRecurrenceFrequenceYearly]; - [theRule setInterval: [self repeat1]]; - - // We recur Every .. of .. - if ([[self repeat2] intValue] == 1) - { - NSString *day; - int occurence; - - day = [theRule iCalRepresentationForWeekDay: [[self repeat6] intValue]]; - occurence = [[self repeat5] intValue] + 1; - if (occurence > 5) // the first/second/third/fourth/fifth .. - occurence = -1; // the last .. - [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", - occurence, day] - forKey: @"byday"]; - [theRule setSingleValue: [NSString stringWithFormat: @"%d", - [[self repeat7] intValue] + 1] - forKey: @"bymonth"]; - } - else - { - if ([[self repeat3] intValue] > 0 - && [[self repeat4] intValue] > 0) - { - values = [[[self repeat3] - componentsSeparatedByString: @","] - mutableCopy]; - [theRule setValues: values atIndex: 0 - forKey: @"bymonthday"]; - [values release]; - [theRule setSingleValue: [NSString stringWithFormat: @"%d", - [[self repeat4] intValue] + 1] - forKey: @"bymonth"]; - } - } - } - } - break; + value: currentEmail]]; } } - - -- (void) takeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext *) _ctx +- (void) setAttributes: (NSDictionary *) data { - SOGoCalendarComponent *clientObject; - iCalRecurrenceRule *rule; NSCalendarDate *now; - - [super takeValuesFromRequest: _rq inContext: _ctx]; + NSMutableDictionary *dataWithOwner; + NSString *owner; now = [NSCalendarDate calendarDate]; - [component setSummary: title]; - [component setLocation: location]; - [component setComment: comment]; - [component setAttach: attachUrl]; - [component setAccessClass: classification]; - [component setCategories: categories]; - [self _handleAttendeesEdition]; + owner = [componentCalendar ownerInContext: context]; + + // Append the calendar's owner to the data as it's required when setting alarms + dataWithOwner = [NSMutableDictionary dictionaryWithDictionary: data]; + [dataWithOwner setObject: owner forKey: @"owner"]; + [component setAttributes: dataWithOwner inContext: context]; + [self _handleOrganizer]; - clientObject = [self clientObject]; - if ([clientObject isNew]) + + if ([[self clientObject] isNew]) { [component setCreated: now]; [component setTimeStampAsDate: now]; } - [component setPriority: priority]; [component setLastModified: now]; - - if (!reminder || [reminder caseInsensitiveCompare: @"-"] == NSOrderedSame) - // No alarm selected -- if there was an unsupported alarm defined in - // the event, it will be deleted. - [component removeAllAlarms]; - else - { - iCalAlarm *anAlarm; - NSString *aValue; - NSUInteger index; - - index = [reminderItems indexOfObject: reminder]; - aValue = [reminderValues objectAtIndex: index]; - - // 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: @""]; - } - else - { - // Custom alarm - anAlarm = [iCalAlarm alarmForEvent: component - owner: [[self clientObject] ownerInContext: context] - action: reminderAction - unit: reminderUnit - quantity: reminderQuantity - reference: reminderReference - reminderRelation: reminderRelation - emailAttendees: reminderEmailAttendees - emailOrganizer: reminderEmailOrganizer]; - } - - if (anAlarm) - { - [component removeAllAlarms]; - [component addToAlarms: anAlarm]; - } - } - - if (![self isChildOccurence]) - { - // We remove any repeat rules - if (!repeat && [component hasRecurrenceRules]) - [component removeAllRecurrenceRules]; - else if ([repeat caseInsensitiveCompare: @"-"] != NSOrderedSame) - { - rule = [iCalRecurrenceRule new]; - - [rule setInterval: @"1"]; - - if ([repeat caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame) - { - [rule setFrequency: iCalRecurrenceFrequenceWeekly]; - [rule setInterval: @"2"]; - } - else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame) - { - [rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]]; - [rule setFrequency: iCalRecurrenceFrequenceDaily]; - } - else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame - || [repeat caseInsensitiveCompare: @"DAILY"] == NSOrderedSame - || [repeat caseInsensitiveCompare: @"WEEKLY"] == NSOrderedSame - || [repeat caseInsensitiveCompare: @"YEARLY"] == NSOrderedSame) - { - [rule setInterval: @"1"]; - [rule setFrequency: - (iCalRecurrenceFrequency) [rule valueForFrequency: repeat]]; - } - else - { - // We have a CUSTOM recurrence. Let's decode what kind of custome recurrence - // we have and set that. - [self _handleCustomRRule: rule]; - } - - [component setRecurrenceRules: [NSArray arrayWithObject: rule]]; - [rule release]; - } - } } -#warning the following methods probably share some code... -- (NSString *) _toolbarForOwner: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject -{ - NSString *toolbarFilename; - BOOL isOrganizer; +//#warning the following methods probably share some code... +//- (NSString *) _toolbarForOwner: (SOGoUser *) ownerUser +// andClientObject: (SOGoContentObject +// *) clientObject +//{ +// NSString *toolbarFilename; +// BOOL isOrganizer; +// +// // We determine if we're the organizer of the component beeing modified. +// // If we created an event on behalf of someone else -userIsOrganizer will +// // return us YES. This is OK because we're in the SENT-BY. But, Alice +// // should be able to accept/decline an invitation if she created the event +// // in Bob's calendar and added herself in the attendee list. +// isOrganizer = [component userIsOrganizer: ownerUser]; +// +// if (isOrganizer) +// isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; +// +// if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]] +// || ([component userIsAttendee: ownerUser] +// && !isOrganizer +// // Lightning does not manage participation status within tasks, +// // so we also ignore the participation status of tasks in the +// // web interface. +// && ![[component tag] isEqualToString: @"VTODO"])) +// toolbarFilename = @"SOGoEmpty.toolbar"; +// else +// { +// if ([clientObject isKindOfClass: [SOGoAppointmentObject class]] +// || [clientObject isKindOfClass: [SOGoAppointmentOccurence class]]) +// toolbarFilename = @"SOGoAppointmentObject.toolbar"; +// else +// toolbarFilename = @"SOGoTaskObject.toolbar"; +// } +// +// return toolbarFilename; +//} +// +//- (NSString *) _toolbarForDelegate: (SOGoUser *) ownerUser +// andClientObject: (SOGoContentObject +// *) clientObject +//{ +// SoSecurityManager *sm; +// NSString *toolbarFilename; +// +// sm = [SoSecurityManager sharedSecurityManager]; +// +// if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent +// onObject: clientObject +// inContext: context]) +// toolbarFilename = [self _toolbarForOwner: ownerUser +// andClientObject: clientObject]; +// else +// toolbarFilename = @"SOGoEmpty.toolbar"; +// +// return toolbarFilename; +//} +// +//- (NSString *) toolbar +//{ +// SOGoContentObject *clientObject; +// NSString *toolbarFilename; +// SOGoUser *ownerUser; +// +// clientObject = [self clientObject]; +// ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context] +// roles: nil]; +// +// if ([ownerUser isEqual: [context activeUser]]) +// toolbarFilename = [self _toolbarForOwner: ownerUser +// andClientObject: clientObject]; +// else +// toolbarFilename = [self _toolbarForDelegate: ownerUser +// andClientObject: clientObject]; +// +// +// return toolbarFilename; +//} +// +// +//- (int) ownerIsAttendee: (SOGoUser *) ownerUser +// andClientObject: (SOGoContentObject +// *) clientObject +//{ +// BOOL isOrganizer; +// iCalPerson *ownerAttendee; +// int rc; +// +// rc = 0; +// +// isOrganizer = [component userIsOrganizer: ownerUser]; +// if (isOrganizer) +// isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; +// +// if (!isOrganizer && ![[component tag] isEqualToString: @"VTODO"]) +// { +// ownerAttendee = [component userAsAttendee: ownerUser]; +// if (ownerAttendee) +// rc = 1; +// } +// +// return rc; +//} +// +//- (int) delegateIsAttendee: (SOGoUser *) ownerUser +// andClientObject: (SOGoContentObject +// *) clientObject +//{ +// SoSecurityManager *sm; +// iCalPerson *ownerAttendee; +// int rc; +// +// rc = 0; +// +// sm = [SoSecurityManager sharedSecurityManager]; +// if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent +// onObject: clientObject +// inContext: context]) +// rc = [self ownerIsAttendee: ownerUser +// andClientObject: clientObject]; +// else if (![sm validatePermission: SOGoCalendarPerm_RespondToComponent +// onObject: clientObject +// 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 +// +// return rc; +//} +// +//- (int) getEventRWType +//{ +// SOGoContentObject *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]; +// } +// +// return rc; +//} +// +//- (BOOL) eventIsReadOnly +//{ +// return [self getEventRWType] != 0; +//} +// +//- (NSString *) emailAlarmsEnabled +//{ +// SOGoSystemDefaults *sd; +// +// sd = [SOGoSystemDefaults sharedSystemDefaults]; +// +// return ([sd enableEMailAlarms] +// ? @"true" +// : @"false"); +//} +// +//- (BOOL) userHasRSVP +//{ +// return ([self getEventRWType] == 1); +//} - // We determine if we're the organizer of the component beeing modified. - // If we created an event on behalf of someone else -userIsOrganizer will - // return us YES. This is OK because we're in the SENT-BY. But, Alice - // should be able to accept/decline an invitation if she created the event - // in Bob's calendar and added herself in the attendee list. - isOrganizer = [component userIsOrganizer: ownerUser]; - - if (isOrganizer) - isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; - - if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]] - || ([component userIsAttendee: ownerUser] - && !isOrganizer - // Lightning does not manage participation status within tasks, - // so we also ignore the participation status of tasks in the - // web interface. - && ![[component tag] isEqualToString: @"VTODO"])) - toolbarFilename = @"SOGoEmpty.toolbar"; - else - { - if ([clientObject isKindOfClass: [SOGoAppointmentObject class]] - || [clientObject isKindOfClass: [SOGoAppointmentOccurence class]]) - toolbarFilename = @"SOGoAppointmentObject.toolbar"; - else - toolbarFilename = @"SOGoTaskObject.toolbar"; - } - - return toolbarFilename; -} - -- (NSString *) _toolbarForDelegate: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject -{ - SoSecurityManager *sm; - NSString *toolbarFilename; - - sm = [SoSecurityManager sharedSecurityManager]; - - if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent - onObject: clientObject - inContext: context]) - toolbarFilename = [self _toolbarForOwner: ownerUser - andClientObject: clientObject]; - else - toolbarFilename = @"SOGoEmpty.toolbar"; - - return toolbarFilename; -} - -- (NSString *) toolbar -{ - SOGoContentObject *clientObject; - NSString *toolbarFilename; - SOGoUser *ownerUser; - - clientObject = [self clientObject]; - ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context] - roles: nil]; - - if ([ownerUser isEqual: [context activeUser]]) - toolbarFilename = [self _toolbarForOwner: ownerUser - andClientObject: clientObject]; - else - toolbarFilename = [self _toolbarForDelegate: ownerUser - andClientObject: clientObject]; - - - return toolbarFilename; -} - - -- (int) ownerIsAttendee: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject -{ - BOOL isOrganizer; - iCalPerson *ownerAttendee; - int rc; - - rc = 0; - - isOrganizer = [component userIsOrganizer: ownerUser]; - if (isOrganizer) - isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; - - if (!isOrganizer && ![[component tag] isEqualToString: @"VTODO"]) - { - ownerAttendee = [component userAsAttendee: ownerUser]; - if (ownerAttendee) - rc = 1; - } - - return rc; -} - -- (int) delegateIsAttendee: (SOGoUser *) ownerUser - andClientObject: (SOGoContentObject - *) clientObject -{ - SoSecurityManager *sm; - iCalPerson *ownerAttendee; - int rc; - - rc = 0; - - sm = [SoSecurityManager sharedSecurityManager]; - if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent - onObject: clientObject - inContext: context]) - rc = [self ownerIsAttendee: ownerUser - andClientObject: clientObject]; - else if (![sm validatePermission: SOGoCalendarPerm_RespondToComponent - onObject: clientObject - 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 - - return rc; -} - -- (int) getEventRWType -{ - SOGoContentObject *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]; - } - - return rc; -} - -- (BOOL) eventIsReadOnly -{ - return [self getEventRWType] != 0; -} - -- (NSString *) emailAlarmsEnabled -{ - SOGoSystemDefaults *sd; - - sd = [SOGoSystemDefaults sharedSystemDefaults]; - - return ([sd enableEMailAlarms] - ? @"true" - : @"false"); -} - -- (BOOL) userHasRSVP -{ - 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]; -} - -- (unsigned int) firstDayOfWeek -{ - SOGoUserDefaults *ud; - - ud = [[context activeUser] userDefaults]; - - return [ud firstDayOfWeek]; -} +//- (unsigned int) firstDayOfWeek +//{ +// SOGoUserDefaults *ud; +// +// ud = [[context activeUser] userDefaults]; +// +// return [ud firstDayOfWeek]; +//} // returns the raw content of the object - (WOResponse *) rawAction