Initial JSON actions to get/set an event

pull/91/head
Francis Lachapelle 2015-02-12 15:01:26 -05:00
parent f694f57638
commit f9271b747c
10 changed files with 1192 additions and 2643 deletions

View File

@ -35,7 +35,7 @@ extern NSNumber *iCalDistantFutureNumber;
+ (void) initializeSOGoExtensions; + (void) initializeSOGoExtensions;
- (NSDictionary *) attributes; - (NSDictionary *) attributesInContext: (WOContext *) context;
- (void) setAttributes: (NSDictionary *) data - (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context; inContext: (WOContext *) context;
@ -44,7 +44,6 @@ extern NSNumber *iCalDistantFutureNumber;
- (iCalPerson *) userAsAttendee: (SOGoUser *) user; - (iCalPerson *) userAsAttendee: (SOGoUser *) user;
- (NSArray *) attendeeUIDs;
- (BOOL) isStillRelevant; - (BOOL) isStillRelevant;
- (id) itipEntryWithMethod: (NSString *) method; - (id) itipEntryWithMethod: (NSString *) method;
@ -52,7 +51,7 @@ extern NSNumber *iCalDistantFutureNumber;
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user; - (NSArray *) attendeesWithoutUser: (SOGoUser *) user;
- (int) priorityNumber; - (int) priorityNumber;
- (NSString *) createdBy; - (NSDictionary *) createdBy;
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date - (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date
withOffset: (int) offset withOffset: (int) offset

View File

@ -44,11 +44,13 @@
#import <NGObjWeb/WOContext+SoObjects.h> #import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h> #import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserManager.h>
#import "iCalPerson+SOGo.h" #import "iCalPerson+SOGo.h"
#import "iCalAlarm+SOGo.h"
#import "iCalCalendar+SOGo.h" #import "iCalCalendar+SOGo.h"
#import "iCalEntityObject+SOGo.h" #import "iCalEntityObject+SOGo.h"
@ -70,18 +72,23 @@ NSNumber *iCalDistantFutureNumber = nil;
/** /**
* @see [UIxAppointmentEditor viewAction] * @see [UIxAppointmentEditor viewAction]
*/ */
- (NSDictionary *) attributes - (NSDictionary *) attributesInContext: (WOContext *) context
{ {
NSArray *elements; NSArray *elements;
NSMutableArray *attendees; NSMutableArray *attendees;
NSDictionary *organizerData; NSDictionary *contactData;
NSMutableDictionary *data, *attendeeData, *alarmData; NSMutableDictionary *data, *organizerData, *attendeeData, *alarmData;
NSEnumerator *attendeesList; NSEnumerator *attendeesList;
NSString *uid, *domain, *sentBy;
NSObject <SOGoSource> *source;
SOGoUserManager *um;
iCalPerson *organizer, *currentAttendee; iCalPerson *organizer, *currentAttendee;
iCalAlarm *alarm; iCalAlarm *alarm;
iCalTrigger *trigger; iCalTrigger *trigger;
id value; id value;
um = [SOGoUserManager sharedUserManager];
data = [NSMutableDictionary dictionaryWithObjectsAndKeys: data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[self tag] lowercaseString], @"component", [[self tag] lowercaseString], @"component",
[self summary], @"summary", [self summary], @"summary",
@ -90,33 +97,46 @@ NSNumber *iCalDistantFutureNumber = nil;
value = [self location]; value = [self location];
if (value) [data setObject: value forKey: @"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"]; value = [self comment];
if ([self accessClass]) [data setObject: [[self accessClass] lowercaseString] forKey: @"classification"]; if (value) [data setObject: value forKey: @"comment"];
if ([self status]) [data setObject: [self status] forKey: @"status"];
if ([self createdBy]) [data setObject: [self createdBy] forKey: @"createdBy"]; 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 // Categories
elements = [self categories]; elements = [self categories];
if ([elements count]) if ([elements count])
[data setObject: elements forKey: @"categories"]; [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"]; 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
organizer = [self organizer]; organizer = [self organizer];
if (organizer) if (organizer)
{ {
organizerData = [NSDictionary dictionaryWithObjectsAndKeys: organizerData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[organizer rfc822Email], @"email", [organizer rfc822Email], @"email",
[organizer cnWithoutQuotes], @"name", [organizer cnWithoutQuotes], @"name",
nil]; 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"]; [data setObject: organizerData forKey: @"organizer"];
} }
// Attendees // Attendees
attendees = [NSMutableArray array]; attendees = [NSMutableArray array];
attendeesList = [[self attendees] objectEnumerator]; attendeesList = [[self attendees] objectEnumerator];
@ -126,29 +146,28 @@ NSNumber *iCalDistantFutureNumber = nil;
[currentAttendee rfc822Email], @"email", [currentAttendee rfc822Email], @"email",
[currentAttendee cnWithoutQuotes], @"name", [currentAttendee cnWithoutQuotes], @"name",
nil]; nil];
if ([currentAttendee uid]) [attendeeData setObject: [currentAttendee uid] forKey: @"uid"]; if ((uid = [currentAttendee uid]))
// TODO: restore support for MS Exchange {
// uid = [um getUIDForEmail: [currentAttendee rfc822Email]]; [attendeeData setObject: uid forKey: @"uid"];
// if (uid != nil) }
// [currentAttendeeData setObject: uid else
// forKey: @"uid"]; {
// else // Search for attendee in global contacts sources that are associated with a MS Exchange server
// { domain = [[context activeUser] domain];
// domain = [[context activeUser] domain]; elements = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain];
// contacts = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain]; if ([elements count] == 1)
// if ([contacts count] == 1) {
// { contactData = [elements lastObject];
// contact = [contacts lastObject]; source = [contactData objectForKey: @"source"];
// source = [contact objectForKey: @"source"]; if ([source conformsToProtocol: @protocol (SOGoDNSource)] &&
// if ([source conformsToProtocol: @protocol (SOGoDNSource)] && [[(NSObject <SOGoDNSource>*) source MSExchangeHostname] length])
// [[(NSObject <SOGoDNSource>*) source MSExchangeHostname] length]) {
// { uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login],
// uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login], [contactData valueForKey: @"c_uid"]];
// [contact valueForKey: @"c_uid"]]; [attendeeData setObject: uid forKey: @"uid"];
// [currentAttendeeData setObject: uid forKey: @"uid"]; }
// } }
// } }
// }
[attendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"status"]; [attendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"status"];
[attendeeData setObject: [[currentAttendee role] lowercaseString] forKey: @"role"]; [attendeeData setObject: [[currentAttendee role] lowercaseString] forKey: @"role"];
if ([[currentAttendee delegatedTo] length]) if ([[currentAttendee delegatedTo] length])
@ -193,6 +212,177 @@ NSNumber *iCalDistantFutureNumber = nil;
return data; 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 - (BOOL) userIsAttendee: (SOGoUser *) user
{ {
NSEnumerator *attendees; NSEnumerator *attendees;
@ -243,10 +433,11 @@ NSNumber *iCalDistantFutureNumber = nil;
if ([mail length]) if ([mail length])
return [user hasEmail: mail]; return [user hasEmail: mail];
return NO; return NO;
} }
/*
- (NSArray *) attendeeUIDs - (NSArray *) attendeeUIDs
{ {
NSEnumerator *attendees; NSEnumerator *attendees;
@ -266,6 +457,7 @@ NSNumber *iCalDistantFutureNumber = nil;
return uids; return uids;
} }
*/
#warning this method should be implemented in a category of iCalToDo #warning this method should be implemented in a category of iCalToDo
- (BOOL) isStillRelevant - (BOOL) isStillRelevant
@ -282,11 +474,11 @@ NSNumber *iCalDistantFutureNumber = nil;
NSArray *events; NSArray *events;
int i, count; int i, count;
newCalendar = [parent mutableCopy]; newCalendar = [parent mutableCopy];
[newCalendar autorelease]; [newCalendar autorelease];
[newCalendar setMethod: method]; [newCalendar setMethod: method];
events = [newCalendar childrenWithTag: tag]; events = [newCalendar childrenWithTag: tag];
count = [events count]; count = [events count];
if (count > 1) if (count > 1)
@ -379,19 +571,39 @@ NSNumber *iCalDistantFutureNumber = nil;
return priorityNumber; 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: @""]; 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]) if (![created_by length])
{ {
// We consider "SENT-BY" in case our custom header isn't found
created_by = [[self organizer] sentBy]; 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; NSCalendarDate *start, *end;
NGCalendarDateRange *range; NGCalendarDateRange *range;
NSMutableArray *alarms; NSMutableArray *alarms;
alarms = [NSMutableArray array]; alarms = [NSMutableArray array];
start = [NSCalendarDate date]; start = [NSCalendarDate date];
end = [start addYear:1 month:0 day:0 hour:0 minute:0 second:0]; end = [start addYear:1 month:0 day:0 hour:0 minute:0 second:0];
range = [NGCalendarDateRange calendarDateRangeWithStartDate: start range = [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end]; endDate: end];
// Always check if container is defined. If not, that means this method // Always check if container is defined. If not, that means this method
// call was reentrant. // call was reentrant.
if (theContainer) if (theContainer)
{ {
NSTimeInterval now; NSTimeInterval now;
int i, v, delta, c_startdate, c_nextalarm; int i, v, delta, c_startdate, c_nextalarm;
// //
// Here is the logic: // 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. // If we don't have a match, we pick the event for which its trigger is the closest to the c_nextalarm.
// //
nextAlarmDate = nil; nextAlarmDate = nil;
[theContainer flattenCycleRecord: (id)row [theContainer flattenCycleRecord: (id)row
forRange: range forRange: range
intoArray: alarms intoArray: alarms
withCalendar: [self parent]]; withCalendar: [self parent]];
// We pickup the closest one from now. We remove the actual reminder (ie., 15 mins before - plus 1 minute for roundups) // 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". // 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 // 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_startdate = [[[alarms objectAtIndex: i] objectForKey: @"c_startdate"] intValue];
c_nextalarm = [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]; c_nextalarm = [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue];
delta = (c_startdate - now - (c_startdate - c_nextalarm)) - 60; delta = (c_startdate - now - (c_startdate - c_nextalarm)) - 60;
// If value is not initialized, we grab it right away // If value is not initialized, we grab it right away
if (!v && delta > 0) if (!v && delta > 0)
v = delta; v = delta;
// If we found a smaller delta than before, use it. // If we found a smaller delta than before, use it.
if (v > 0 && delta > 0 && delta <= v) 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"]]; o = [[self parent] eventWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
else else
o = [[self parent] todoWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]]; o = [[self parent] todoWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
if (!o) if (!o)
o = self; o = self;
if ([[o alarms] count]) if ([[o alarms] count])
{ {
anAlarm = [self firstDisplayOrAudioAlarm]; anAlarm = [self firstDisplayOrAudioAlarm];
@ -551,7 +763,7 @@ NSNumber *iCalDistantFutureNumber = nil;
if (!webstatus if (!webstatus
|| ([webstatus caseInsensitiveCompare: @"TRIGGERED"] || ([webstatus caseInsensitiveCompare: @"TRIGGERED"]
!= NSOrderedSame)) != NSOrderedSame))
v = delta; v = delta;
nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]]; nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
} }
@ -565,7 +777,7 @@ NSNumber *iCalDistantFutureNumber = nil;
} // if (theContainer) } // if (theContainer)
} }
} }
if ([nextAlarmDate isNotNull]) if ([nextAlarmDate isNotNull])
[row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]] [row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]]
forKey: @"c_nextalarm"]; forKey: @"c_nextalarm"];

View File

@ -1,6 +1,6 @@
/* iCalEvent+SOGo.h - this file is part of SOGo /* 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 * 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 * it under the terms of the GNU General Public License as published by
@ -28,7 +28,7 @@
@interface iCalEvent (SOGoExtensions) @interface iCalEvent (SOGoExtensions)
- (BOOL) isStillRelevant; - (BOOL) isStillRelevant;
- (unsigned int) occurenceInterval; - (NSTimeInterval) occurenceInterval;
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
@end @end

View File

@ -1,6 +1,6 @@
/* iCalEvent+SOGo.m - this file is part of SOGo /* 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 * 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 * it under the terms of the GNU General Public License as published by
@ -22,12 +22,14 @@
#import <Foundation/NSCalendarDate.h> #import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h> #import <Foundation/NSEnumerator.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h> #import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZone.h> #import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalEvent.h> #import <NGCards/iCalEvent.h>
@ -36,6 +38,10 @@
#import <NGCards/iCalTrigger.h> #import <NGCards/iCalTrigger.h>
#import <NGCards/NSString+NGCards.h> #import <NGCards/NSString+NGCards.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import "SOGoAppointmentFolder.h" #import "SOGoAppointmentFolder.h"
#import "iCalRepeatableEntityObject+SOGo.h" #import "iCalRepeatableEntityObject+SOGo.h"
@ -47,7 +53,7 @@
{ {
NSCalendarDate *now, *lastRecurrence; NSCalendarDate *now, *lastRecurrence;
BOOL isStillRelevent; BOOL isStillRelevent;
now = [NSCalendarDate calendarDate]; now = [NSCalendarDate calendarDate];
if ([self isRecurrent]) if ([self isRecurrent])
@ -57,7 +63,7 @@
} }
else else
isStillRelevent = ([[self endDate] earlierDate: now] == now); isStillRelevent = ([[self endDate] earlierDate: now] == now);
return isStillRelevent; return isStillRelevent;
} }
@ -114,8 +120,8 @@
row = [NSMutableDictionary dictionaryWithCapacity:8]; row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vevent" forKey: @"c_component"]; [row setObject: @"vevent" forKey: @"c_component"];
if ([uid isNotNull]) if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"]; [row setObject:uid forKey: @"c_uid"];
else else
[self logWithFormat: @"WARNING: could not extract a uid from event!"]; [self logWithFormat: @"WARNING: could not extract a uid from event!"];
@ -168,11 +174,11 @@
forAllDay: isAllDay] forAllDay: isAllDay]
forKey: @"c_enddate"]; forKey: @"c_enddate"];
} }
if ([self isRecurrent]) if ([self isRecurrent])
{ {
NSCalendarDate *date; NSCalendarDate *date;
date = [self lastPossibleRecurrenceStartDate]; date = [self lastPossibleRecurrenceStartDate];
if (!date) if (!date)
{ {
@ -194,7 +200,7 @@
if ([status isNotNull]) if ([status isNotNull])
{ {
int code = 1; int code = 1;
if ([status isEqualToString: @"TENTATIVE"]) if ([status isEqualToString: @"TENTATIVE"])
code = 2; code = 2;
else if ([status isEqualToString: @"CANCELLED"]) else if ([status isEqualToString: @"CANCELLED"])
@ -214,12 +220,12 @@
if (organizer) if (organizer)
{ {
NSString *email; NSString *email;
email = [organizer valueForKey: @"rfc822Email"]; email = [organizer valueForKey: @"rfc822Email"];
if (email) if (email)
[row setObject:email forKey: @"c_orgmail"]; [row setObject:email forKey: @"c_orgmail"];
} }
/* construct partstates */ /* construct partstates */
count = [attendees count]; count = [attendees count];
partstates = [[NSMutableString alloc] initWithCapacity:count * 2]; partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
@ -227,7 +233,7 @@
{ {
iCalPerson *p; iCalPerson *p;
iCalPersonPartStat stat; iCalPersonPartStat stat;
p = [attendees objectAtIndex:i]; p = [attendees objectAtIndex:i];
stat = [p participationStatus]; stat = [p participationStatus];
if (i) 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. * with respect to the previous end date of the event.
* @param previousEndDate 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 [iCalEntityObject+SOGo attributes]
* @see [UIxAppointmentEditor viewAction] * @see [UIxAppointmentEditor viewAction]
*/ */
- (NSDictionary *) attributes - (NSDictionary *) attributesInContext: (WOContext *) context
{ {
NSMutableDictionary *data; NSMutableDictionary *data;
@ -329,4 +388,100 @@
return data; 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 @end

View File

@ -37,6 +37,10 @@
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import "iCalRepeatableEntityObject+SOGo.h" #import "iCalRepeatableEntityObject+SOGo.h"
@implementation iCalRepeatableEntityObject (SOGoExtensions) @implementation iCalRepeatableEntityObject (SOGoExtensions)
@ -76,7 +80,7 @@
* @see [iCalEvent+SOGo attributes] * @see [iCalEvent+SOGo attributes]
* @see [UIxAppointmentEditor viewAction] * @see [UIxAppointmentEditor viewAction]
*/ */
- (NSDictionary *) attributes - (NSDictionary *) attributesInContext: (WOContext *) context
{ {
NSMutableDictionary *data, *repeat; NSMutableDictionary *data, *repeat;
NSArray *rules; NSArray *rules;
@ -110,6 +114,103 @@
return data; 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 - (NSString *) cycleInfo
{ {
NSArray *rules; NSArray *rules;
@ -124,7 +225,7 @@
rules = [self _indexedRules: [self recurrenceRules]]; rules = [self _indexedRules: [self recurrenceRules]];
if (rules) if (rules)
[cycleInfo setObject: rules forKey: @"rules"]; [cycleInfo setObject: rules forKey: @"rules"];
rules = [self _indexedRules: [self exceptionRules]]; rules = [self _indexedRules: [self exceptionRules]];
if (rules) if (rules)
[cycleInfo setObject: rules forKey: @"exRules"]; [cycleInfo setObject: rules forKey: @"exRules"];
@ -163,7 +264,7 @@
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end]; endDate: end];
} }
return firstRange; return firstRange;
} }

View File

@ -301,13 +301,6 @@
} }
else else
data = @""; data = @"";
else if ([data isKindOfClass: [NSArray class]])
{
if ([data count] > 0)
data = [data objectAtIndex: 0];
else
data = @"";
}
[newRecord setObject: data forKey: @"c_telephonenumber"]; [newRecord setObject: data forKey: @"c_telephonenumber"];
// Custom attribute for group-lookups. See LDAPSource.m where // Custom attribute for group-lookups. See LDAPSource.m where

View File

@ -1,6 +1,6 @@
/* UIxAppointmentEditor.h - this file is part of SOGo /* 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 * 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 * it under the terms of the GNU General Public License as published by
@ -24,43 +24,14 @@
#import <SOGoUI/UIxComponent.h> #import <SOGoUI/UIxComponent.h>
@class iCalEvent; @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; SOGoDateFormatter *dateFormatter;
} }
/* template values */
- (NSString *) saveURL;
- (iCalEvent *) event; - (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 @end
#endif /* UIXAPPOINTMENTEDITOR_H */ #endif /* UIXAPPOINTMENTEDITOR_H */

View File

@ -33,6 +33,7 @@
#import <NGObjWeb/NSException+HTTP.h> #import <NGObjWeb/NSException+HTTP.h>
#import <NGExtensions/NSCalendarDate+misc.h> #import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NGCalendarDateRange.h> #import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
#import <NGCards/iCalAlarm.h> #import <NGCards/iCalAlarm.h>
@ -73,15 +74,6 @@
if ((self = [super init])) if ((self = [super init]))
{ {
aptStartDate = nil;
aptEndDate = nil;
item = nil;
event = nil;
isAllDay = NO;
isTransparent = NO;
sendAppointmentNotifications = YES;
componentCalendar = nil;
user = [[self context] activeUser]; user = [[self context] activeUser];
ASSIGN (dateFormatter, [user dateFormatterInContext: context]); ASSIGN (dateFormatter, [user dateFormatterInContext: context]);
} }
@ -91,116 +83,16 @@
- (void) dealloc - (void) dealloc
{ {
[item release];
[[event parent] release];
[aptStartDate release];
[aptEndDate release];
[dateFormatter release]; [dateFormatter release];
[componentCalendar release];
[super dealloc]; [super dealloc];
} }
/* template values */
- (iCalEvent *) event - (iCalEvent *) event
{ {
if (!event) return (iCalEvent *) component;
{
event = (iCalEvent *) [[self clientObject] occurence];
[[event parent] retain];
}
return event;
} }
- (NSString *) rsvpURL - (NSString *) _dateString: (NSCalendarDate *) date
{
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
{ {
char buf[22]; char buf[22];
NSNumber *day, *month, *year; NSNumber *day, *month, *year;
@ -217,14 +109,7 @@
return [NSString stringWithCString:buf]; return [NSString stringWithCString:buf];
} }
/*
/* read-only event */
- (BOOL) startDateIsEqualToEndDate
{
return [aptStartDate isEqualToDate: aptEndDate];
}
/* actions */
- (NSCalendarDate *) newStartDate - (NSCalendarDate *) newStartDate
{ {
NSCalendarDate *newStartDate, *now; NSCalendarDate *newStartDate, *now;
@ -262,7 +147,9 @@
return newStartDate; return newStartDate;
} }
*/
/*
- (id <WOActionResults>) defaultAction - (id <WOActionResults>) defaultAction
{ {
NSCalendarDate *startDate, *endDate; NSCalendarDate *startDate, *endDate;
@ -316,7 +203,7 @@
endDate = [endDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 endDate = [endDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
seconds:-offset]; seconds:-offset];
} }
isTransparent = ![event isOpaque]; isTransparent = ![event isOpaque];
sendAppointmentNotifications = ([event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"] ? NO : YES); sendAppointmentNotifications = ([event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"] ? NO : YES);
} }
@ -328,15 +215,18 @@
return self; return self;
} }
*/
- (void) _adjustRecurrentRules - (void) _adjustRecurrentRules
{ {
iCalEvent *event;
iCalRecurrenceRule *rule; iCalRecurrenceRule *rule;
NSEnumerator *rules; NSEnumerator *rules;
NSCalendarDate *untilDate; NSCalendarDate *untilDate;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
NSTimeZone *timeZone; NSTimeZone *timeZone;
event = [self event];
rules = [[event recurrenceRules] objectEnumerator]; rules = [[event recurrenceRules] objectEnumerator];
ud = [[context activeUser] userDefaults]; ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone]; timeZone = [ud timeZone];
@ -492,66 +382,151 @@
return response; 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 <WOActionResults>) saveAction - (id <WOActionResults>) saveAction
{ {
NSDictionary *params;
NSString *jsonResponse;
NSException *ex;
iCalEvent *event;
SOGoAppointmentFolder *previousCalendar; SOGoAppointmentFolder *previousCalendar;
SOGoAppointmentObject *co; SOGoAppointmentObject *co;
NSString *jsonResponse;
SoSecurityManager *sm; SoSecurityManager *sm;
NSException *ex; WORequest *request;
event = [self event];
co = [self clientObject]; co = [self clientObject];
if ([co isKindOfClass: [SOGoAppointmentOccurence class]]) if ([co isKindOfClass: [SOGoAppointmentOccurence class]])
co = [co container]; co = [co container];
previousCalendar = [co container]; previousCalendar = [co container];
sm = [SoSecurityManager sharedSecurityManager]; sm = [SoSecurityManager sharedSecurityManager];
ex = nil; ex = nil;
request = [context request];
if ([event hasRecurrenceRules]) params = [[request contentAsString] objectFromJSONString];
[self _adjustRecurrentRules]; if (params == nil)
if ([co isNew])
{ {
if (componentCalendar ex = [NSException exceptionWithName: @"JSONParsingException"
&& ![[componentCalendar ocsPath] reason: @"Can't parse JSON string"
isEqualToString: [previousCalendar ocsPath]]) userInfo: nil];
{
// 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];
} }
else else
{ {
// The event was modified -- save it. [self setAttributes: params];
ex = [co saveComponent: event];
if (componentCalendar if ([event hasRecurrenceRules])
&& ![[componentCalendar ocsPath] [self _adjustRecurrentRules];
isEqualToString: [previousCalendar ocsPath]])
if ([co isNew])
{ {
// The event was moved to a different calendar. if (componentCalendar
if (![sm validatePermission: SoPerm_DeleteObjects && ![[componentCalendar ocsPath]
onObject: previousCalendar isEqualToString: [previousCalendar ocsPath]])
inContext: context])
{ {
// 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 if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: componentCalendar onObject: componentCalendar
inContext: context]) inContext: context])
ex = [co moveToFolder: componentCalendar]; 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 else
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
@"success", @"status", nil]; @"success", @"status", nil];
return [self responseWithStatus: 200 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} endDate End date (YYYY-MM-DD)
* @apiSuccess (Success 200) {String} localizedEndDate Formatted end date * @apiSuccess (Success 200) {String} localizedEndDate Formatted end date
* @apiSuccess (Success 200) {String} endTime Formatted end time * @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} isAllDay 1 if event is all-day
* @apiSuccess (Success 200) {Number} isTransparent 1 if the event is not opaque * @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] * 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} component "vevent"
* @apiSuccess (Success 200) {String} summary Summary * @apiSuccess (Success 200) {String} summary Summary
* @apiSuccess (Success 200) {String} location Location * @apiSuccess (Success 200) {String} [location] Location
* @apiSuccess (Success 200) {String} comment Comment * @apiSuccess (Success 200) {String} [comment] Comment
* @apiSuccess (Success 200) {String} [status] Status
* @apiSuccess (Success 200) {String} [attachUrl] Attached URL * @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) {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) {String[]} [categories] Categories
* @apiSuccess (Success 200) {Object} [organizer] Appointment organizer * @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.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) {Object[]} [attendees] List of attendees
* @apiSuccess (Success 200) {String} [attendees.name] Attendee's name * @apiSuccess (Success 200) {String} [attendees.name] Attendee's name
* @apiSuccess (Success 200) {String} attendees.email Attendee's email address * @apiSuccess (Success 200) {String} attendees.email Attendee's email address
* @apiSuccess (Success 200) {String} [attendees.uid] System user ID * @apiSuccess (Success 200) {String} [attendees.uid] System user ID
* @apiSuccess (Success 200) {String} attendees.status Attendee's participation status * @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.delegatedTo] User that the original request was delegated to
* @apiSuccess (Success 200) {String} [attendees.delegatedFrom] User the request was delegated from * @apiSuccess (Success 200) {String} [attendees.delegatedFrom] User the request was delegated from
* @apiSuccess (Success 200) {Object[]} [alarm] Alarm definition * @apiSuccess (Success 200) {Object[]} [alarm] Alarm definition
* @apiSuccess (Success 200) {String} alarm.action Either display or email * @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.unit Either MINUTES, HOURS, or DAYS
* @apiSuccess (Success 200) {String} alarm.reference Either BEFORE or AFTER * @apiSuccess (Success 200) {String} alarm.reference Either BEFORE or AFTER
* @apiSuccess (Success 200) {String} alarm.relation Either START or END * @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) {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.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) {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) {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.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) * @apiSuccess (Success 200) {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31)
*/ */
@ -648,11 +629,14 @@
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
SOGoCalendarComponent *co; SOGoCalendarComponent *co;
iCalAlarm *anAlarm; iCalAlarm *anAlarm;
iCalEvent *event;
BOOL resetAlarm; BOOL resetAlarm;
unsigned int snoozeAlarm; unsigned int snoozeAlarm;
[self event]; // [self component];
// [self componentCalendar];
event = [self event];
ud = [[context activeUser] userDefaults]; ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone]; timeZone = [ud timeZone];
@ -661,14 +645,6 @@
[eventStartDate setTimeZone: timeZone]; [eventStartDate setTimeZone: timeZone];
[eventEndDate setTimeZone: timeZone]; [eventEndDate setTimeZone: timeZone];
co = [self clientObject]; 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 // 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 // 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. // If either is set, we must find the right alarm.
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue]; resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue]; snoozeAlarm = [[[context request] formValueForKey: @"snoozeAlarm"] intValue];
if (resetAlarm || snoozeAlarm) if (resetAlarm || snoozeAlarm)
{ {
iCalEvent *master; iCalEvent *master;
@ -686,7 +662,7 @@
timezone: timeZone timezone: timeZone
startDate: &eventStartDate startDate: &eventStartDate
endDate: &eventEndDate]; endDate: &eventEndDate];
anAlarm = [event firstDisplayOrAudioAlarm]; anAlarm = [event firstDisplayOrAudioAlarm];
if (resetAlarm) if (resetAlarm)
@ -694,7 +670,7 @@
iCalTrigger *aTrigger; iCalTrigger *aTrigger;
aTrigger = [anAlarm trigger]; aTrigger = [anAlarm trigger];
[aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"]; [aTrigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"];
[co saveComponent: master]; [co saveComponent: master];
} }
else if (snoozeAlarm) else if (snoozeAlarm)
@ -707,10 +683,10 @@
[co nameInContainer], @"id", [co nameInContainer], @"id",
[componentCalendar nameInContainer], @"pid", [componentCalendar nameInContainer], @"pid",
[componentCalendar displayName], @"calendar", [componentCalendar displayName], @"calendar",
[self formattedDateString: eventStartDate], @"startDate", [self _dateString: eventStartDate], @"startDate",
[dateFormatter formattedDate: eventStartDate], @"localizedStartDate", [dateFormatter formattedDate: eventStartDate], @"localizedStartDate",
[dateFormatter formattedTime: eventStartDate], @"startTime", [dateFormatter formattedTime: eventStartDate], @"startTime",
[self formattedDateString: eventEndDate], @"endDate", [self _dateString: eventEndDate], @"endDate",
[dateFormatter formattedDate: eventEndDate], @"localizedEndDate", [dateFormatter formattedDate: eventEndDate], @"localizedEndDate",
[dateFormatter formattedTime: eventEndDate], @"endTime", [dateFormatter formattedTime: eventEndDate], @"endTime",
nil]; nil];
@ -722,78 +698,4 @@
return [self responseWithStatus: 200 andJSONRepresentation: data]; 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 @end

View File

@ -23,167 +23,17 @@
#import <SOGoUI/UIxComponent.h> #import <SOGoUI/UIxComponent.h>
@class NSArray;
@class NSCalendarDate;
@class NSDictionary; @class NSDictionary;
@class NSString;
@class iCalPerson;
@class iCalRecurrenceRule;
@class iCalRepeatableEntityObject; @class iCalRepeatableEntityObject;
@interface UIxComponentEditor : UIxComponent @interface UIxComponentEditor : UIxComponent
{ {
iCalRepeatableEntityObject *component; iCalRepeatableEntityObject *component;
id item;
id attendee;
NSString *rsvpURL;
NSString *saveURL;
NSMutableArray *calendarList;
NSDictionary *organizerProfile;
/* individual values */
NSCalendarDate *cycleUntilDate;
NSString *title;
NSString *location;
SOGoAppointmentFolder *componentCalendar; 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) setAttributes: (NSDictionary *) attributes;
- (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;
+ (NSArray *) reminderValues; + (NSArray *) reminderValues;

File diff suppressed because it is too large Load Diff