2006-06-15 21:34:10 +02:00
|
|
|
|
/*
|
|
|
|
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
|
|
|
|
|
|
|
|
|
This file is part of OpenGroupware.org.
|
|
|
|
|
|
|
|
|
|
OGo is free software; you can redistribute it and/or modify it under
|
|
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
|
later version.
|
|
|
|
|
|
|
|
|
|
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
License along with OGo; see the file COPYING. If not, write to the
|
|
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
|
02111-1307, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
#import <NGCards/NSString+NGCards.h>
|
|
|
|
|
|
2006-08-15 22:59:06 +02:00
|
|
|
|
#import <SOGoUI/UIxComponent.h>
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
/* TODO: CLEAN UP */
|
|
|
|
|
|
|
|
|
|
@class NSString;
|
|
|
|
|
@class iCalPerson;
|
|
|
|
|
@class iCalRecurrenceRule;
|
|
|
|
|
|
|
|
|
|
@interface UIxAppointmentEditor : UIxComponent
|
|
|
|
|
{
|
|
|
|
|
NSString *iCalString;
|
|
|
|
|
NSString *errorText;
|
|
|
|
|
id item;
|
|
|
|
|
|
|
|
|
|
/* individual values */
|
|
|
|
|
NSCalendarDate *startDate;
|
|
|
|
|
NSCalendarDate *endDate;
|
|
|
|
|
NSCalendarDate *cycleUntilDate;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *title;
|
|
|
|
|
NSString *location;
|
|
|
|
|
NSString *comment;
|
|
|
|
|
iCalPerson *organizer;
|
|
|
|
|
NSArray *participants; /* array of iCalPerson's */
|
|
|
|
|
NSArray *resources; /* array of iCalPerson's */
|
|
|
|
|
NSString *priority;
|
|
|
|
|
NSArray *categories;
|
|
|
|
|
NSString *accessClass;
|
|
|
|
|
BOOL isPrivate; /* default: NO */
|
|
|
|
|
BOOL checkForConflicts; /* default: NO */
|
|
|
|
|
NSDictionary *cycle;
|
|
|
|
|
NSString *cycleEnd;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalStringTemplate;
|
|
|
|
|
- (NSString *)iCalString;
|
|
|
|
|
|
|
|
|
|
- (void)setIsPrivate:(BOOL)_yn;
|
|
|
|
|
- (void)setAccessClass:(NSString *)_class;
|
|
|
|
|
|
|
|
|
|
- (void)setCheckForConflicts:(BOOL)_checkForConflicts;
|
|
|
|
|
- (BOOL)checkForConflicts;
|
|
|
|
|
|
|
|
|
|
- (BOOL)hasCycle;
|
|
|
|
|
- (iCalRecurrenceRule *)rrule;
|
|
|
|
|
- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule;
|
|
|
|
|
- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule;
|
|
|
|
|
|
|
|
|
|
- (BOOL)isCycleEndUntil;
|
|
|
|
|
- (void)setIsCycleEndUntil;
|
|
|
|
|
- (void)setIsCycleEndNever;
|
|
|
|
|
|
|
|
|
|
- (NSString *)_completeURIForMethod:(NSString *)_method;
|
|
|
|
|
|
|
|
|
|
- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
|
|
|
|
|
treatAsResource:(BOOL)_isResource;
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters;
|
|
|
|
|
- (NSString *)iCalParticipantsStringFromQueryParameters;
|
|
|
|
|
- (NSString *)iCalResourcesStringFromQueryParameters;
|
|
|
|
|
- (NSString *)iCalStringFromQueryParameter:(NSString *)_qp
|
|
|
|
|
format:(NSString *)_format;
|
|
|
|
|
- (NSString *)iCalOrganizerString;
|
|
|
|
|
|
|
|
|
|
- (id)acceptOrDeclineAction:(BOOL)_accept;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2006-09-29 20:31:20 +02:00
|
|
|
|
#import "common.h"
|
|
|
|
|
#import <NGCards/NGCards.h>
|
|
|
|
|
#import <NGExtensions/NGCalendarDateRange.h>
|
|
|
|
|
#import <SOGoUI/SOGoDateFormatter.h>
|
|
|
|
|
#import <SOGo/AgenorUserManager.h>
|
|
|
|
|
#import <Appointments/SOGoAppointmentFolder.h>
|
|
|
|
|
#import <Appointments/SOGoAppointmentObject.h>
|
|
|
|
|
#import "UIxComponent+Agenor.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
@interface NSDate(UsedPrivates)
|
2006-09-13 23:20:57 +02:00
|
|
|
|
- (NSString *)icalString; // TODO: this is in NGCards
|
2006-06-15 21:34:10 +02:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation UIxAppointmentEditor
|
|
|
|
|
|
|
|
|
|
+ (int)version {
|
|
|
|
|
return [super version] + 0 /* v2 */;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)initialize {
|
|
|
|
|
NSAssert2([super version] == 2,
|
|
|
|
|
@"invalid superclass (%@) version %i !",
|
|
|
|
|
NSStringFromClass([self superclass]), [super version]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)init {
|
|
|
|
|
self = [super init];
|
|
|
|
|
if(self) {
|
|
|
|
|
[self setIsPrivate:NO];
|
|
|
|
|
[self setCheckForConflicts:NO];
|
|
|
|
|
[self setIsCycleEndNever];
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)dealloc {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
[iCalString release];
|
|
|
|
|
[errorText release];
|
|
|
|
|
[item release];
|
|
|
|
|
|
|
|
|
|
[startDate release];
|
|
|
|
|
[endDate release];
|
|
|
|
|
[cycleUntilDate release];
|
|
|
|
|
[title release];
|
|
|
|
|
[location release];
|
|
|
|
|
[organizer release];
|
|
|
|
|
[comment release];
|
|
|
|
|
[participants release];
|
|
|
|
|
[resources release];
|
|
|
|
|
[priority release];
|
|
|
|
|
[categories release];
|
|
|
|
|
[accessClass release];
|
|
|
|
|
[cycle release];
|
|
|
|
|
[cycleEnd release];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
|
|
|
|
|
- (void)setItem:(id)_item {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(item, _item);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (id)item {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return item;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setErrorText:(NSString *)_txt {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(errorText, _txt);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)errorText {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return errorText;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (BOOL)hasErrorText {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return [errorText length] > 0 ? YES : NO;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSFormatter *)titleDateFormatter {
|
|
|
|
|
SOGoDateFormatter *fmt;
|
|
|
|
|
|
|
|
|
|
fmt = [[[SOGoDateFormatter alloc] initWithLocale:[self locale]] autorelease];
|
|
|
|
|
[fmt setFullWeekdayNameAndDetails];
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setAptStartDate:(NSCalendarDate *)_date {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(startDate, _date);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSCalendarDate *)aptStartDate {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return startDate;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (void)setAptEndDate:(NSCalendarDate *)_date {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(endDate, _date);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSCalendarDate *)aptEndDate {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return endDate;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setTitle:(NSString *)_value {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(title, _value);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)title {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return title;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (void)setLocation:(NSString *)_value {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(location, _value);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)location {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return location;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (void)setComment:(NSString *)_value {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(comment, _value);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)comment {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return comment;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setParticipants:(NSArray *)_parts {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(participants, _parts);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSArray *)participants {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return participants;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (void)setResources:(NSArray *)_res {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(resources, _res);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSArray *)resources {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return resources;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* priorities */
|
|
|
|
|
|
|
|
|
|
- (NSArray *)priorities {
|
|
|
|
|
/* 0 == undefined
|
|
|
|
|
5 == normal
|
|
|
|
|
1 == high
|
|
|
|
|
*/
|
|
|
|
|
static NSArray *priorities = nil;
|
|
|
|
|
|
|
|
|
|
if (!priorities)
|
|
|
|
|
priorities = [[NSArray arrayWithObjects:@"0", @"5", @"1", nil] retain];
|
|
|
|
|
return priorities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)itemPriorityText {
|
|
|
|
|
NSString *key;
|
|
|
|
|
|
2006-09-07 00:31:38 +02:00
|
|
|
|
key = [NSString stringWithFormat:@"prio_%@", item];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return [self labelForKey:key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setPriority:(NSString *)_priority {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(priority, _priority);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)priority {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return priority;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* categories */
|
|
|
|
|
|
|
|
|
|
- (NSArray *)categoryItems {
|
|
|
|
|
// TODO: make this configurable?
|
|
|
|
|
/*
|
|
|
|
|
Tasks categories will be modified as follow :
|
|
|
|
|
– by default (a simple logo or no logo at all),
|
|
|
|
|
– appointment,
|
|
|
|
|
– outside,
|
|
|
|
|
– meeting,
|
|
|
|
|
– holidays,
|
|
|
|
|
– phone.
|
|
|
|
|
*/
|
|
|
|
|
static NSArray *categoryItems = nil;
|
|
|
|
|
|
|
|
|
|
if (!categoryItems) {
|
|
|
|
|
categoryItems = [[NSArray arrayWithObjects:@"APPOINTMENT",
|
|
|
|
|
@"NOT IN OFFICE",
|
|
|
|
|
@"MEETING",
|
|
|
|
|
@"HOLIDAY",
|
|
|
|
|
@"PHONE CALL",
|
|
|
|
|
nil] retain];
|
|
|
|
|
}
|
|
|
|
|
return categoryItems;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-15 22:59:06 +02:00
|
|
|
|
- (NSString *) itemCategoryText {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return [[self labelForKey: item] stringByEscapingHTMLString];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setCategories:(NSArray *)_categories {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(categories, _categories);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
2006-08-15 22:59:06 +02:00
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
- (NSArray *)categories {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return categories;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* class */
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
- (NSArray *)accessClassItems {
|
|
|
|
|
static NSArray classItems = nil;
|
|
|
|
|
|
|
|
|
|
if (!classItems) {
|
|
|
|
|
return [[NSArray arrayWithObjects:@"PUBLIC", @"PRIVATE", nil] retain];
|
|
|
|
|
}
|
|
|
|
|
return classItems;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
- (void)setAccessClass:(NSString *)_class {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(accessClass, _class);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)accessClass {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return accessClass;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setIsPrivate:(BOOL)_yn {
|
|
|
|
|
if (_yn)
|
|
|
|
|
[self setAccessClass:@"PRIVATE"];
|
|
|
|
|
else
|
|
|
|
|
[self setAccessClass:@"PUBLIC"];
|
2006-09-07 00:31:38 +02:00
|
|
|
|
isPrivate = _yn;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (BOOL)isPrivate {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return isPrivate;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setCheckForConflicts:(BOOL)_checkForConflicts {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
checkForConflicts = _checkForConflicts;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (BOOL)checkForConflicts {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return checkForConflicts;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray *)cycles {
|
|
|
|
|
static NSArray *cycles = nil;
|
|
|
|
|
|
|
|
|
|
if (!cycles) {
|
|
|
|
|
NSBundle *bundle;
|
|
|
|
|
NSString *path;
|
|
|
|
|
|
|
|
|
|
bundle = [NSBundle bundleForClass:[self class]];
|
|
|
|
|
path = [bundle 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 {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGN(cycle, _cycle);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSDictionary *)cycle {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return cycle;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (BOOL)hasCycle {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
[self debugWithFormat:@"cycle: %@", cycle];
|
|
|
|
|
if (![cycle objectForKey:@"rule"])
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return NO;
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
- (NSString *)cycleLabel {
|
|
|
|
|
NSString *key;
|
|
|
|
|
|
2006-09-07 00:31:38 +02:00
|
|
|
|
key = [(NSDictionary *)item objectForKey:@"label"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return [self labelForKey:key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setCycleUntilDate:(NSCalendarDate *)_cycleUntilDate {
|
|
|
|
|
NSCalendarDate *until;
|
|
|
|
|
|
|
|
|
|
/* copy hour/minute/second from startDate */
|
2006-09-07 00:31:38 +02:00
|
|
|
|
until = [_cycleUntilDate hour:[startDate hourOfDay]
|
|
|
|
|
minute:[startDate minuteOfHour]
|
|
|
|
|
second:[startDate secondOfMinute]];
|
|
|
|
|
[until setTimeZone:[startDate timeZone]];
|
|
|
|
|
ASSIGN(cycleUntilDate, until);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSCalendarDate *)cycleUntilDate {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return cycleUntilDate;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (iCalRecurrenceRule *)rrule {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *ruleRep;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
iCalRecurrenceRule *rule;
|
|
|
|
|
|
|
|
|
|
if (![self hasCycle])
|
|
|
|
|
return nil;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ruleRep = [cycle objectForKey:@"rule"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep];
|
|
|
|
|
|
2006-09-07 00:31:38 +02:00
|
|
|
|
if (cycleUntilDate && [self isCycleEndUntil])
|
|
|
|
|
[rule setUntilDate:cycleUntilDate];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSDictionary *c;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
NSCalendarDate *until;
|
|
|
|
|
|
|
|
|
|
c = [self cycleMatchingRRule:_rrule];
|
|
|
|
|
[self setCycle:c];
|
|
|
|
|
|
|
|
|
|
until = [[[_rrule untilDate] copy] autorelease];
|
|
|
|
|
if (!until)
|
2006-09-07 00:31:38 +02:00
|
|
|
|
until = startDate;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
else
|
|
|
|
|
[self setIsCycleEndUntil];
|
|
|
|
|
|
2006-09-08 17:37:02 +02:00
|
|
|
|
[until setTimeZone:[[self clientObject] userTimeZone]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[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;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSArray *cycles;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
|
|
if (!_rrule)
|
|
|
|
|
return [[self cycles] objectAtIndex:0];
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
cycleRep = [_rrule versitString];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
cycles = [self cycles];
|
|
|
|
|
count = [cycles count];
|
|
|
|
|
for (i = 1; i < count; i++) {
|
|
|
|
|
NSDictionary *c;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *cr;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
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 alloc] initWithObjects:@"cycle_end_never",
|
|
|
|
|
@"cycle_end_until",
|
|
|
|
|
nil];
|
|
|
|
|
}
|
|
|
|
|
return ends;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setCycleEnd:(NSString *)_cycleEnd {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(cycleEnd, _cycleEnd);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)cycleEnd {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return cycleEnd;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (BOOL)isCycleEndUntil {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return (cycleEnd &&
|
|
|
|
|
[cycleEnd isEqualToString:@"cycle_end_until"]);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (void)setIsCycleEndUntil {
|
|
|
|
|
[self setCycleEnd:@"cycle_end_until"];
|
|
|
|
|
}
|
|
|
|
|
- (void)setIsCycleEndNever {
|
|
|
|
|
[self setCycleEnd:@"cycle_end_never"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* transparency */
|
|
|
|
|
|
|
|
|
|
- (NSString *)transparency {
|
|
|
|
|
return @"TRANSPARENT";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* iCal */
|
|
|
|
|
|
|
|
|
|
- (void)setICalString:(NSString *)_s {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
ASSIGNCOPY(iCalString, _s);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
- (NSString *)iCalString {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
return iCalString;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalStringTemplate {
|
|
|
|
|
static NSString *iCalStringTemplate = \
|
|
|
|
|
@"BEGIN:VCALENDAR\r\n"
|
|
|
|
|
@"METHOD:REQUEST\r\n"
|
|
|
|
|
@"PRODID:OpenGroupware.org SOGo 0.9\r\n"
|
|
|
|
|
@"VERSION:2.0\r\n"
|
|
|
|
|
@"BEGIN:VEVENT\r\n"
|
|
|
|
|
@"UID:%@\r\n"
|
|
|
|
|
@"CLASS:PUBLIC\r\n"
|
|
|
|
|
@"STATUS:CONFIRMED\r\n" /* confirmed by default */
|
|
|
|
|
@"DTSTAMP:%@\r\n"
|
|
|
|
|
@"DTSTART:%@\r\n"
|
|
|
|
|
@"DTEND:%@\r\n"
|
|
|
|
|
@"TRANSP:%@\r\n"
|
|
|
|
|
@"SEQUENCE:1\r\n"
|
|
|
|
|
@"PRIORITY:5\r\n"
|
|
|
|
|
@"%@" /* organizer */
|
|
|
|
|
@"%@" /* participants and resources */
|
|
|
|
|
@"END:VEVENT\r\n"
|
|
|
|
|
@"END:VCALENDAR";
|
|
|
|
|
|
|
|
|
|
NSCalendarDate *lStartDate, *lEndDate;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *template, *s;
|
|
|
|
|
unsigned minutes;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
s = [self queryParameterForKey:@"dur"];
|
|
|
|
|
if(s && [s length] > 0) {
|
|
|
|
|
minutes = [s intValue];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
minutes = 60;
|
|
|
|
|
}
|
|
|
|
|
lStartDate = [self selectedDate];
|
|
|
|
|
lEndDate = [lStartDate dateByAddingYears:0 months:0 days:0
|
|
|
|
|
hours:0 minutes:minutes seconds:0];
|
|
|
|
|
|
|
|
|
|
s = [self iCalParticipantsAndResourcesStringFromQueryParameters];
|
|
|
|
|
template = [NSString stringWithFormat:iCalStringTemplate,
|
2006-08-31 01:11:50 +02:00
|
|
|
|
[[self clientObject] nameInContainer],
|
|
|
|
|
[[NSCalendarDate date] icalString],
|
|
|
|
|
[lStartDate icalString],
|
|
|
|
|
[lEndDate icalString],
|
|
|
|
|
[self transparency],
|
|
|
|
|
[self iCalOrganizerString],
|
|
|
|
|
s];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return template;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters {
|
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
s = [self iCalParticipantsStringFromQueryParameters];
|
|
|
|
|
return [s stringByAppendingString:
|
|
|
|
|
[self iCalResourcesStringFromQueryParameters]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalParticipantsStringFromQueryParameters {
|
|
|
|
|
static NSString *iCalParticipantString = \
|
|
|
|
|
@"ATTENDEE;ROLE=REQ-PARTICIPANT;CN=\"%@\":mailto:%@\r\n";
|
|
|
|
|
|
|
|
|
|
return [self iCalStringFromQueryParameter:@"ps"
|
|
|
|
|
format:iCalParticipantString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalResourcesStringFromQueryParameters {
|
|
|
|
|
static NSString *iCalResourceString = \
|
|
|
|
|
@"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":mailto:%@\r\n";
|
|
|
|
|
|
|
|
|
|
return [self iCalStringFromQueryParameter:@"rs"
|
|
|
|
|
format:iCalResourceString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalStringFromQueryParameter:(NSString *)_qp
|
|
|
|
|
format:(NSString *)_format
|
|
|
|
|
{
|
|
|
|
|
AgenorUserManager *um;
|
|
|
|
|
NSMutableString *iCalRep;
|
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
um = [AgenorUserManager sharedUserManager];
|
|
|
|
|
iCalRep = (NSMutableString *)[NSMutableString string];
|
|
|
|
|
s = [self queryParameterForKey:_qp];
|
|
|
|
|
if(s && [s length] > 0) {
|
|
|
|
|
NSArray *es;
|
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
|
|
es = [s componentsSeparatedByString:@","];
|
|
|
|
|
count = [es count];
|
|
|
|
|
for(i = 0; i < count; i++) {
|
|
|
|
|
NSString *email, *cn;
|
|
|
|
|
|
|
|
|
|
email = [es objectAtIndex:i];
|
|
|
|
|
cn = [um getCNForUID:[um getUIDForEmail:email]];
|
|
|
|
|
[iCalRep appendFormat:_format, cn, email];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return iCalRep;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)iCalOrganizerString {
|
|
|
|
|
static NSString *fmt = @"ORGANIZER;CN=\"%@\":mailto:%@\r\n";
|
|
|
|
|
return [NSString stringWithFormat:fmt,
|
|
|
|
|
[self cnForUser],
|
|
|
|
|
[self emailForUser]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
- (iCalPerson *)getOrganizer {
|
|
|
|
|
iCalPerson *p;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *emailProp;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
|
|
|
|
|
p = [[[iCalPerson alloc] init] autorelease];
|
|
|
|
|
[p setEmail:emailProp];
|
|
|
|
|
[p setCn:[self cnForUser]];
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* helper */
|
|
|
|
|
|
|
|
|
|
- (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];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* new */
|
|
|
|
|
|
|
|
|
|
- (id)newAction {
|
|
|
|
|
/*
|
|
|
|
|
This method creates a unique ID and redirects to the "edit" method on the
|
|
|
|
|
new ID.
|
|
|
|
|
It is actually a folder method and should be defined on the folder.
|
|
|
|
|
|
|
|
|
|
Note: 'clientObject' is the SOGoAppointmentFolder!
|
|
|
|
|
Update: remember that there are group folders as well.
|
|
|
|
|
*/
|
|
|
|
|
NSString *uri, *objectId, *method, *ps;
|
|
|
|
|
|
|
|
|
|
objectId = [NSClassFromString(@"SOGoAppointmentFolder")
|
|
|
|
|
globallyUniqueObjectId];
|
|
|
|
|
if ([objectId length] == 0) {
|
|
|
|
|
return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
|
|
|
|
|
reason:@"could not create a unique ID"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
method = [NSString stringWithFormat:@"Calendar/%@/edit", objectId];
|
|
|
|
|
method = [[self userFolderPath] stringByAppendingPathComponent:method];
|
|
|
|
|
|
|
|
|
|
/* check if participants have already been provided */
|
2006-08-14 22:43:55 +02:00
|
|
|
|
ps = [self queryParameterForKey:@"ps"];
|
|
|
|
|
// if (ps) {
|
|
|
|
|
// [self setQueryParameter:ps forKey:@"ps"];
|
|
|
|
|
// }
|
|
|
|
|
if (!ps
|
|
|
|
|
&& [[self clientObject] respondsToSelector:@selector(calendarUIDs)]) {
|
2006-06-15 21:34:10 +02:00
|
|
|
|
AgenorUserManager *um;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSArray *uids;
|
|
|
|
|
NSMutableArray *emails;
|
|
|
|
|
unsigned i, count;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
/* add all current calendarUIDs as default participants */
|
|
|
|
|
|
|
|
|
|
um = [AgenorUserManager sharedUserManager];
|
|
|
|
|
uids = [[self clientObject] calendarUIDs];
|
|
|
|
|
count = [uids count];
|
|
|
|
|
emails = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
NSString *email;
|
|
|
|
|
|
|
|
|
|
email = [um getEmailForUID:[uids objectAtIndex:i]];
|
|
|
|
|
if (email)
|
|
|
|
|
[emails addObject:email];
|
|
|
|
|
}
|
|
|
|
|
ps = [emails componentsJoinedByString:@","];
|
|
|
|
|
[self setQueryParameter:ps forKey:@"ps"];
|
|
|
|
|
}
|
|
|
|
|
uri = [self completeHrefForMethod:method];
|
|
|
|
|
return [self redirectToLocation:uri];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* save */
|
|
|
|
|
|
|
|
|
|
/* returned dates are in GMT */
|
|
|
|
|
- (NSCalendarDate *)_dateFromString:(NSString *)_str {
|
|
|
|
|
NSCalendarDate *date;
|
|
|
|
|
|
|
|
|
|
date = [NSCalendarDate dateWithString:_str
|
|
|
|
|
calendarFormat:@"%Y-%m-%d %H:%M %Z"];
|
2006-09-08 17:37:02 +02:00
|
|
|
|
[date setTimeZone:[[self clientObject] serverTimeZone]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return date;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
|
|
|
|
|
treatAsResource:(BOOL)_isResource
|
|
|
|
|
{
|
|
|
|
|
unsigned i, count;
|
|
|
|
|
NSMutableArray *result;
|
|
|
|
|
|
|
|
|
|
count = [_values count];
|
|
|
|
|
result = [[NSMutableArray alloc] initWithCapacity:count];
|
|
|
|
|
for (i = 0; i < count; i++) {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *pString, *email, *cn;
|
|
|
|
|
NSRange r;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
iCalPerson *p;
|
|
|
|
|
|
|
|
|
|
pString = [_values objectAtIndex:i];
|
|
|
|
|
if ([pString length] == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* delimiter between email and cn */
|
|
|
|
|
r = [pString rangeOfString:@";"];
|
|
|
|
|
if (r.length > 0) {
|
|
|
|
|
email = [pString substringToIndex:r.location];
|
|
|
|
|
cn = (r.location + 1 < [pString length])
|
|
|
|
|
? [pString substringFromIndex:r.location + 1]
|
|
|
|
|
: nil;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
email = pString;
|
|
|
|
|
cn = nil;
|
|
|
|
|
}
|
|
|
|
|
if (cn == nil) {
|
|
|
|
|
/* fallback */
|
|
|
|
|
AgenorUserManager *um = [AgenorUserManager sharedUserManager];
|
|
|
|
|
cn = [um getCNForUID:[um getUIDForEmail:email]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = [[iCalPerson alloc] init];
|
|
|
|
|
[p setEmail:[@"mailto:" stringByAppendingString:email]];
|
|
|
|
|
if ([cn isNotNull]) [p setCn:cn];
|
|
|
|
|
|
|
|
|
|
/* see RFC2445, sect. 4.2.16 for details */
|
|
|
|
|
[p setRole:_isResource ? @"NON-PARTICIPANT" : @"REQ-PARTICIPANT"];
|
|
|
|
|
[result addObject:p];
|
|
|
|
|
[p release];
|
|
|
|
|
}
|
|
|
|
|
return [result autorelease];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isWriteableClientObject {
|
|
|
|
|
return [[self clientObject]
|
|
|
|
|
respondsToSelector:@selector(saveContentString:)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSException *)validateObjectForStatusChange {
|
|
|
|
|
BOOL ok;
|
|
|
|
|
id co;
|
|
|
|
|
|
|
|
|
|
co = [self clientObject];
|
|
|
|
|
ok = [co respondsToSelector:@selector(changeParticipationStatus:inContext:)];
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
|
|
|
|
|
reason:
|
|
|
|
|
@"method cannot be invoked on the specified object"];
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
- (void)loadValuesFromAppointment: (iCalEvent *)_appointment
|
|
|
|
|
{
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *s;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
iCalRecurrenceRule *rrule;
|
|
|
|
|
|
2006-09-07 00:31:38 +02:00
|
|
|
|
if ((startDate = [[_appointment startDate] copy]) == nil)
|
|
|
|
|
startDate = [[[NSCalendarDate date] hour:11 minute:0] copy];
|
|
|
|
|
if ((endDate = [[_appointment endDate] copy]) == nil) {
|
|
|
|
|
endDate =
|
|
|
|
|
[[startDate hour:[startDate hourOfDay] + 1 minute:0] copy];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
2006-09-08 17:37:02 +02:00
|
|
|
|
[startDate setTimeZone:[[self clientObject] userTimeZone]];
|
|
|
|
|
[endDate setTimeZone:[[self clientObject] userTimeZone]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
2006-09-07 00:31:38 +02:00
|
|
|
|
title = [[_appointment summary] copy];
|
|
|
|
|
location = [[_appointment location] copy];
|
|
|
|
|
comment = [[_appointment comment] copy];
|
|
|
|
|
priority = [[_appointment priority] copy];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
categories = [[[_appointment categories] commaSeparatedValues] retain];
|
2006-09-07 00:31:38 +02:00
|
|
|
|
organizer = [[_appointment organizer] retain];
|
|
|
|
|
participants = [[_appointment participants] retain];
|
|
|
|
|
resources = [[_appointment resources] retain];
|
|
|
|
|
|
2006-09-08 17:37:02 +02:00
|
|
|
|
// NSLog (@"summary <20><><EFBFBD>: '%@'", title);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
s = [_appointment accessClass];
|
|
|
|
|
if(!s || [s isEqualToString:@"PUBLIC"])
|
|
|
|
|
[self setIsPrivate:NO];
|
|
|
|
|
else
|
|
|
|
|
[self setIsPrivate:YES]; /* we're possibly loosing information here */
|
|
|
|
|
|
|
|
|
|
/* cycles */
|
2006-09-28 19:26:02 +02:00
|
|
|
|
if ([_appointment isRecurrent])
|
|
|
|
|
{
|
|
|
|
|
rrule = [[_appointment recurrenceRules] objectAtIndex: 0];
|
|
|
|
|
[self adjustCycleControlsForRRule:rrule];
|
|
|
|
|
}
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
- (void)saveValuesIntoAppointment:(iCalEvent *)_appointment {
|
2006-06-15 21:34:10 +02:00
|
|
|
|
/* merge in form values */
|
|
|
|
|
NSArray *attendees, *lResources;
|
2006-09-28 19:26:02 +02:00
|
|
|
|
iCalRecurrenceRule *rrule;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
[_appointment setStartDate:[self aptStartDate]];
|
|
|
|
|
[_appointment setEndDate:[self aptEndDate]];
|
2006-09-07 00:31:38 +02:00
|
|
|
|
|
|
|
|
|
[_appointment setSummary: [self title]];
|
|
|
|
|
[_appointment setLocation: [self location]];
|
|
|
|
|
[_appointment setComment: [self comment]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[_appointment setPriority:[self priority]];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
[_appointment setCategories: [[self categories] componentsJoinedByString: @","]];
|
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[_appointment setAccessClass:[self accessClass]];
|
|
|
|
|
[_appointment setTransparency:[self transparency]];
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/*
|
|
|
|
|
Note: bad, bad, bad!
|
|
|
|
|
Organizer is no form value, thus we MUST NOT change it
|
|
|
|
|
*/
|
2006-09-07 00:31:38 +02:00
|
|
|
|
[_appointment setOrganizer:organizer];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
#endif
|
|
|
|
|
attendees = [self participants];
|
|
|
|
|
lResources = [self resources];
|
|
|
|
|
if ([lResources count] > 0) {
|
|
|
|
|
attendees = ([attendees count] > 0)
|
|
|
|
|
? [attendees arrayByAddingObjectsFromArray:lResources]
|
|
|
|
|
: lResources;
|
|
|
|
|
}
|
2006-09-28 19:26:02 +02:00
|
|
|
|
[attendees makeObjectsPerformSelector: @selector (setTag:)
|
|
|
|
|
withObject: @"attendee"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[_appointment setAttendees:attendees];
|
|
|
|
|
|
|
|
|
|
/* cycles */
|
2006-09-28 19:26:02 +02:00
|
|
|
|
[_appointment removeAllRecurrenceRules];
|
|
|
|
|
rrule = [self rrule];
|
|
|
|
|
if (rrule)
|
|
|
|
|
[_appointment addToRecurrenceRules: rrule];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
- (iCalEvent *) appointmentFromString: (NSString *) _iCalString
|
|
|
|
|
{
|
|
|
|
|
iCalCalendar *calendar;
|
|
|
|
|
iCalEvent *appointment;
|
|
|
|
|
SOGoAppointmentObject *clientObject;
|
|
|
|
|
|
|
|
|
|
clientObject = [self clientObject];
|
2006-09-29 20:31:20 +02:00
|
|
|
|
calendar = [iCalCalendar parseSingleFromSource: _iCalString];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
appointment = [clientObject firstEventFromCalendar: calendar];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
return appointment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) loadValuesFromICalString: (NSString *) _iCalString
|
|
|
|
|
{
|
|
|
|
|
[self loadValuesFromAppointment: [self appointmentFromString: _iCalString]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* contact editor compatibility */
|
|
|
|
|
|
|
|
|
|
- (void)setContentString:(NSString *)_s {
|
|
|
|
|
[self setICalString:_s];
|
|
|
|
|
}
|
|
|
|
|
- (NSString *)contentStringTemplate {
|
|
|
|
|
return [self iCalStringTemplate];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)loadValuesFromContentString:(NSString *)_s {
|
|
|
|
|
[self loadValuesFromICalString:_s];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* access */
|
|
|
|
|
|
2006-09-29 20:31:20 +02:00
|
|
|
|
- (BOOL) isMyApt
|
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
|
// TODO: this should check a set of emails against the SoUser
|
2006-09-29 20:31:20 +02:00
|
|
|
|
return (![[organizer email] length]
|
|
|
|
|
|| [[organizer rfc822Email] isEqualToString: [self emailForUser]]);
|
2006-06-15 21:34:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)canAccessApt {
|
|
|
|
|
return [self isMyApt];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)canEditApt {
|
|
|
|
|
return [self isMyApt];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* conflict management */
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
- (BOOL)containsConflict:(iCalEvent *)_apt {
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSArray *attendees, *uids;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
SOGoAppointmentFolder *groupCalendar;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSArray *infos;
|
|
|
|
|
NSArray *ranges;
|
|
|
|
|
id folder;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
[self logWithFormat:@"search from %@ to %@",
|
|
|
|
|
[_apt startDate], [_apt endDate]];
|
|
|
|
|
|
|
|
|
|
folder = [[self clientObject] container];
|
|
|
|
|
attendees = [_apt attendees];
|
|
|
|
|
uids = [folder uidsFromICalPersons:attendees];
|
|
|
|
|
if ([uids count] == 0) {
|
|
|
|
|
[self logWithFormat:@"Note: no UIDs selected."];
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groupCalendar = [folder lookupGroupCalendarFolderForUIDs:uids
|
|
|
|
|
inContext:[self context]];
|
|
|
|
|
[self debugWithFormat:@"group calendar: %@", groupCalendar];
|
|
|
|
|
|
|
|
|
|
if (![groupCalendar respondsToSelector:@selector(fetchFreebusyInfosFrom:to:)]) {
|
|
|
|
|
[self errorWithFormat:@"invalid folder to run freebusy query on!"];
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infos = [groupCalendar fetchFreebusyInfosFrom:[_apt startDate]
|
|
|
|
|
to:[_apt endDate]];
|
|
|
|
|
[self debugWithFormat:@" process: %d events", [infos count]];
|
|
|
|
|
|
|
|
|
|
ranges = [infos arrayByCreatingDateRangesFromObjectsWithStartDateKey:@"startDate"
|
|
|
|
|
andEndDateKey:@"endDate"];
|
|
|
|
|
ranges = [ranges arrayByCompactingContainedDateRanges];
|
|
|
|
|
[self debugWithFormat:@" blocked ranges: %@", ranges];
|
|
|
|
|
|
|
|
|
|
return [ranges count] != 0 ? YES : NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* response generation */
|
|
|
|
|
|
|
|
|
|
- (NSString *)initialCycleVisibility {
|
|
|
|
|
if (![self hasCycle])
|
|
|
|
|
return @"visibility: hidden;";
|
|
|
|
|
return @"visibility: visible;";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)initialCycleEndUntilVisibility {
|
|
|
|
|
if ([self isCycleEndUntil])
|
|
|
|
|
return @"visibility: visible;";
|
|
|
|
|
return @"visibility: hidden;";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* actions */
|
|
|
|
|
|
|
|
|
|
- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)testAction {
|
|
|
|
|
/* for testing only */
|
2006-09-07 00:31:38 +02:00
|
|
|
|
WORequest *req;
|
2006-09-28 19:26:02 +02:00
|
|
|
|
iCalEvent *apt;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
NSString *content;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
req = [[self context] request];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
apt = [self appointmentFromString: [self iCalString]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[self saveValuesIntoAppointment:apt];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
content = [[apt parent] versitString];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
[self logWithFormat:@"%s -- iCal:\n%@",
|
|
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
|
content];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id<WOActionResults>)defaultAction {
|
|
|
|
|
NSString *ical;
|
|
|
|
|
|
|
|
|
|
/* load iCalendar file */
|
|
|
|
|
|
|
|
|
|
// TODO: can't we use [clientObject contentAsString]?
|
2006-09-28 19:26:02 +02:00
|
|
|
|
// ical = [[self clientObject] valueForKey:@"iCalString"];
|
|
|
|
|
ical = [[self clientObject] contentAsString];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
if ([ical length] == 0) /* a new appointment */
|
|
|
|
|
ical = [self contentStringTemplate];
|
|
|
|
|
|
|
|
|
|
[self setContentString:ical];
|
|
|
|
|
[self loadValuesFromContentString:ical];
|
|
|
|
|
|
|
|
|
|
if (![self canEditApt]) {
|
|
|
|
|
/* TODO: we need proper ACLs */
|
|
|
|
|
return [self redirectToLocation:[self _completeURIForMethod:@"../view"]];
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)saveAction {
|
2006-09-28 19:26:02 +02:00
|
|
|
|
iCalEvent *apt;
|
2006-09-07 00:31:38 +02:00
|
|
|
|
iCalPerson *p;
|
|
|
|
|
NSString *content;
|
|
|
|
|
NSException *ex;
|
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
if (![self isWriteableClientObject]) {
|
|
|
|
|
/* return 400 == Bad Request */
|
|
|
|
|
return [NSException exceptionWithHTTPStatus:400
|
|
|
|
|
reason:@"method cannot be invoked on "
|
|
|
|
|
@"the specified object"];
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-28 19:26:02 +02:00
|
|
|
|
apt = [self appointmentFromString: [self iCalString]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
if (apt == nil) {
|
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
s = [self labelForKey:@"Invalid iCal data!"];
|
|
|
|
|
[self setErrorText:s];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self saveValuesIntoAppointment:apt];
|
|
|
|
|
p = [apt findParticipantWithEmail:[self emailForUser]];
|
|
|
|
|
if (p) {
|
|
|
|
|
[p setParticipationStatus:iCalPersonPartStatAccepted];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([self checkForConflicts]) {
|
|
|
|
|
if ([self containsConflict:apt]) {
|
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
s = [self labelForKey:@"Conflicts found!"];
|
|
|
|
|
[self setErrorText:s];
|
2006-09-28 19:26:02 +02:00
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-28 19:26:02 +02:00
|
|
|
|
content = [[apt parent] versitString];
|
|
|
|
|
// [apt release]; apt = nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
|
|
if (content == nil) {
|
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
|
|
s = [self labelForKey:@"Could not create iCal data!"];
|
|
|
|
|
[self setErrorText:s];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ex = [[self clientObject] saveContentString:content];
|
|
|
|
|
if (ex != nil) {
|
|
|
|
|
[self setErrorText:[ex reason]];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [self redirectToLocation:[self _completeURIForMethod:@".."]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)acceptAction {
|
|
|
|
|
return [self acceptOrDeclineAction:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)declineAction {
|
|
|
|
|
return [self acceptOrDeclineAction:NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: add tentatively
|
|
|
|
|
|
|
|
|
|
- (id)acceptOrDeclineAction:(BOOL)_accept {
|
|
|
|
|
// TODO: this should live in the SoObjects
|
|
|
|
|
NSException *ex;
|
|
|
|
|
|
|
|
|
|
if ((ex = [self validateObjectForStatusChange]) != nil)
|
|
|
|
|
return ex;
|
|
|
|
|
|
|
|
|
|
ex = [[self clientObject] changeParticipationStatus:
|
|
|
|
|
_accept ? @"ACCEPTED" : @"DECLINED"
|
|
|
|
|
inContext:[self context]];
|
|
|
|
|
if (ex != nil) return ex;
|
|
|
|
|
|
|
|
|
|
return [self redirectToLocation:[self _completeURIForMethod:@"../view"]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end /* UIxAppointmentEditor */
|