From 9ec06c4bb92d5a33725e222a2afc0ac1d74fa49d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Wed, 18 Oct 2006 14:42:44 +0000 Subject: [PATCH] Monotone-Parent: 71bb185f5f96c21cf62ca4dd2cadf54d83dc4f7c Monotone-Revision: 11fad7a10b612bfb5d7852bc31ad9fa992c6a07b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2006-10-18T14:42:44 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 10 + UI/Scheduler/UIxAppointmentEditor.h | 72 ++- UI/Scheduler/UIxAppointmentEditor.m | 645 ++------------------------ UI/Scheduler/UIxComponentEditor.h | 151 ++++++ UI/Scheduler/UIxComponentEditor.m | 692 ++++++++++++++++++++++++++++ UI/Scheduler/UIxTaskEditor.h | 74 ++- UI/Scheduler/UIxTaskEditor.m | 671 ++------------------------- 7 files changed, 981 insertions(+), 1334 deletions(-) create mode 100644 UI/Scheduler/UIxComponentEditor.h create mode 100644 UI/Scheduler/UIxComponentEditor.m diff --git a/ChangeLog b/ChangeLog index 3fc4ce73a..b0a7b8a2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2006-10-18 Wolfgang Sourdeau + + * UI/Scheduler/UIxAppointmentEditor.m, + UI/Scheduler/UIxTaskEditor.m: + refactored to user UIxComponentEditor as parent class. + + * UI/Scheduler/UIxComponentEditor.[hm]: new class module + containing the methods common to the UIxTaskEditor and the + UIxAppointmentEditor. + 2006-10-17 Wolfgang Sourdeau * SoObjects/Appointments/SOGoAppointmentFolder.m diff --git a/UI/Scheduler/UIxAppointmentEditor.h b/UI/Scheduler/UIxAppointmentEditor.h index 076dece39..612de9ead 100644 --- a/UI/Scheduler/UIxAppointmentEditor.h +++ b/UI/Scheduler/UIxAppointmentEditor.h @@ -23,68 +23,50 @@ #ifndef UIXAPPOINTMENTEDITOR_H #define UIXAPPOINTMENTEDITOR_H -#import +#import @class NSString; @class iCalPerson; @class iCalRecurrenceRule; -@interface UIxAppointmentEditor : UIxComponent +@interface UIxAppointmentEditor : UIxComponentEditor { - NSString *iCalString; - NSString *errorText; - id item; - - /* individual values */ - NSCalendarDate *startDate; NSCalendarDate *endDate; - NSCalendarDate *cycleUntilDate; - 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; } -- (NSString *)iCalStringTemplate; -- (NSString *)iCalString; +- (void) setAptStartDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) aptStartDate; -- (void)setIsPrivate:(BOOL)_yn; -- (void)setAccessClass:(NSString *)_class; +- (void) setAptEndDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) aptEndDate; -- (void)setCheckForConflicts:(BOOL)_checkForConflicts; -- (BOOL)checkForConflicts; +/* iCal */ -- (BOOL)hasCycle; -- (iCalRecurrenceRule *)rrule; -- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule; -- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule; +- (NSString *) iCalStringTemplate; -- (BOOL)isCycleEndUntil; -- (void)setIsCycleEndUntil; -- (void)setIsCycleEndNever; +/* new */ -- (NSString *)_completeURIForMethod:(NSString *)_method; +- (id) newAction; -- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values - treatAsResource:(BOOL)_isResource; +/* save */ -- (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters; -- (NSString *)iCalParticipantsStringFromQueryParameters; -- (NSString *)iCalResourcesStringFromQueryParameters; -- (NSString *)iCalStringFromQueryParameter:(NSString *)_qp - format:(NSString *)_format; -- (NSString *)iCalOrganizerString; +- (void) loadValuesFromAppointment: (iCalEvent *) _apt; +- (void) saveValuesIntoAppointment: (iCalEvent *) _apt; +- (iCalEvent *) appointmentFromString: (NSString *) _iCalString; -- (id)acceptOrDeclineAction:(BOOL)_accept; +/* conflict management */ + +- (BOOL) containsConflict: (id) _apt; +- (id ) defaultAction; +- (id ) saveAction; +- (id) acceptAction; +- (id) declineAction; + +- (NSString *) saveUrl; + +// TODO: add tentatively + +- (id) acceptOrDeclineAction: (BOOL) _accept; @end diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 23592e679..5e61dfa6f 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -22,13 +22,13 @@ #import #import +#import #import #import "common.h" #import #import #import -#import #import #import #import "UIxComponent+Agenor.h" @@ -49,375 +49,42 @@ NSStringFromClass([self superclass]), [super version]); } -- (id)init { - self = [super init]; - if(self) { - [self setIsPrivate:NO]; - [self setCheckForConflicts:NO]; - [self setIsCycleEndNever]; - } - return self; -} - -- (void)dealloc { - [iCalString release]; - [errorText release]; - [item release]; - - [startDate release]; +- (void) dealloc +{ [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]; [super dealloc]; } /* accessors */ -- (void)setItem:(id)_item { - ASSIGN(item, _item); -} -- (id)item { - return item; +- (void) setAptStartDate: (NSCalendarDate *)_date +{ + [self setStartDate: _date]; } -- (void)setErrorText:(NSString *)_txt { - ASSIGNCOPY(errorText, _txt); -} -- (NSString *)errorText { - return errorText; -} -- (BOOL)hasErrorText { - return [errorText length] > 0 ? YES : NO; +- (NSCalendarDate *) aptStartDate +{ + return [self startDate]; } -- (NSFormatter *)titleDateFormatter { - SOGoDateFormatter *fmt; - - fmt = [[[SOGoDateFormatter alloc] initWithLocale:[self locale]] autorelease]; - [fmt setFullWeekdayNameAndDetails]; - return fmt; -} - -- (void)setAptStartDate:(NSCalendarDate *)_date { - ASSIGN(startDate, _date); -} -- (NSCalendarDate *)aptStartDate { - return startDate; -} -- (void)setAptEndDate:(NSCalendarDate *)_date { +- (void) setAptEndDate: (NSCalendarDate *) _date +{ ASSIGN(endDate, _date); } -- (NSCalendarDate *)aptEndDate { + +- (NSCalendarDate *) aptEndDate +{ return endDate; } -- (void)setTitle:(NSString *)_value { - ASSIGNCOPY(title, _value); -} -- (NSString *)title { - return title; -} -- (void)setLocation:(NSString *)_value { - ASSIGNCOPY(location, _value); -} -- (NSString *)location { - return location; -} -- (void)setComment:(NSString *)_value { - ASSIGNCOPY(comment, _value); -} -- (NSString *)comment { - return comment; -} - -- (void)setParticipants:(NSArray *)_parts { - ASSIGN(participants, _parts); -} -- (NSArray *)participants { - return participants; -} -- (void)setResources:(NSArray *)_res { - ASSIGN(resources, _res); -} -- (NSArray *)resources { - return resources; -} - -/* 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; - - key = [NSString stringWithFormat:@"prio_%@", item]; - return [self labelForKey:key]; -} - -- (void)setPriority:(NSString *)_priority { - ASSIGN(priority, _priority); -} -- (NSString *)priority { - return priority; -} - - -/* 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; -} - -- (NSString *) itemCategoryText { - return [[self labelForKey: item] stringByEscapingHTMLString]; -} - -- (void)setCategories:(NSArray *)_categories { - ASSIGN(categories, _categories); -} - -- (NSArray *)categories { - return categories; -} - -/* 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 { - ASSIGN(accessClass, _class); -} -- (NSString *)accessClass { - return accessClass; -} - -- (void)setIsPrivate:(BOOL)_yn { - if (_yn) - [self setAccessClass:@"PRIVATE"]; - else - [self setAccessClass:@"PUBLIC"]; - isPrivate = _yn; -} -- (BOOL)isPrivate { - return isPrivate; -} - -- (void)setCheckForConflicts:(BOOL)_checkForConflicts { - checkForConflicts = _checkForConflicts; -} -- (BOOL)checkForConflicts { - return checkForConflicts; -} - -- (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 { - ASSIGN(cycle, _cycle); -} -- (NSDictionary *)cycle { - return cycle; -} -- (BOOL)hasCycle { - [self debugWithFormat:@"cycle: %@", cycle]; - if (![cycle objectForKey:@"rule"]) - return NO; - return YES; -} -- (NSString *)cycleLabel { - NSString *key; - - key = [(NSDictionary *)item objectForKey:@"label"]; - return [self labelForKey:key]; -} - - -- (void)setCycleUntilDate:(NSCalendarDate *)_cycleUntilDate { - NSCalendarDate *until; - - /* copy hour/minute/second from startDate */ - until = [_cycleUntilDate hour:[startDate hourOfDay] - minute:[startDate minuteOfHour] - second:[startDate secondOfMinute]]; - [until setTimeZone:[startDate timeZone]]; - ASSIGN(cycleUntilDate, until); -} -- (NSCalendarDate *)cycleUntilDate { - return cycleUntilDate; -} - -- (iCalRecurrenceRule *)rrule { - NSString *ruleRep; - iCalRecurrenceRule *rule; - - if (![self hasCycle]) - return nil; - ruleRep = [cycle objectForKey:@"rule"]; - rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; - - if (cycleUntilDate && [self isCycleEndUntil]) - [rule setUntilDate:cycleUntilDate]; - return rule; -} - -- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule { - NSDictionary *c; - NSCalendarDate *until; - - c = [self cycleMatchingRRule:_rrule]; - [self setCycle:c]; - - until = [[[_rrule untilDate] copy] autorelease]; - if (!until) - until = startDate; - else - [self setIsCycleEndUntil]; - - [until setTimeZone:[[self clientObject] userTimeZone]]; - [self setCycleUntilDate:until]; -} - -/* - This method is necessary, because we have a fixed sets of cycles in the UI. - The model is able to represent arbitrary rules, however. - There SHOULD be a different UI, similar to iCal.app, to allow modelling - of more complex rules. - - This method obviously cannot map all existing rules back to the fixed list - in cycles.plist. This should be fixed in a future version when interop - becomes more important. - */ -- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule { - NSString *cycleRep; - NSArray *cycles; - unsigned i, count; - - if (!_rrule) - return [[self cycles] objectAtIndex:0]; - - cycleRep = [_rrule versitString]; - cycles = [self cycles]; - count = [cycles count]; - for (i = 1; i < count; i++) { - NSDictionary *c; - NSString *cr; - - c = [cycles objectAtIndex:i]; - cr = [c objectForKey:@"rule"]; - if ([cr isEqualToString:cycleRep]) - return c; - } - [self warnWithFormat:@"No default cycle for rrule found! -> %@", _rrule]; - return nil; -} - -/* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */ -- (NSArray *)cycleEnds { - static NSArray *ends = nil; - - if (!ends) { - ends = [[NSArray alloc] initWithObjects:@"cycle_end_never", - @"cycle_end_until", - nil]; - } - return ends; -} - -- (void)setCycleEnd:(NSString *)_cycleEnd { - ASSIGNCOPY(cycleEnd, _cycleEnd); -} -- (NSString *)cycleEnd { - return cycleEnd; -} -- (BOOL)isCycleEndUntil { - return (cycleEnd && - [cycleEnd isEqualToString:@"cycle_end_until"]); -} -- (void)setIsCycleEndUntil { - [self setCycleEnd:@"cycle_end_until"]; -} -- (void)setIsCycleEndNever { - [self setCycleEnd:@"cycle_end_never"]; -} - /* transparency */ - (NSString *)transparency { return @"TRANSPARENT"; } - /* iCal */ -- (void)setICalString:(NSString *)_s { - ASSIGNCOPY(iCalString, _s); -} -- (NSString *)iCalString { - return iCalString; -} - - (NSString *)iCalStringTemplate { static NSString *iCalStringTemplate = \ @"BEGIN:VCALENDAR\r\n" @@ -473,105 +140,10 @@ 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; - NSString *emailProp; - - 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 { +- (id) newAction +{ /* This method creates a unique ID and redirects to the "edit" method on the new ID. @@ -627,117 +199,20 @@ /* save */ -/* returned dates are in GMT */ -- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values - treatAsResource:(BOOL)_isResource +- (void) loadValuesFromAppointment: (iCalEvent *) appointment { - unsigned i, count; - NSMutableArray *result; - - count = [_values count]; - result = [[NSMutableArray alloc] initWithCapacity:count]; - for (i = 0; i < count; i++) { - NSString *pString, *email, *cn; - NSRange r; - 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; -} - -- (void)loadValuesFromAppointment: (iCalEvent *)_appointment -{ - NSString *s; - iCalRecurrenceRule *rrule; NSTimeZone *uTZ; - if ((startDate = [_appointment startDate]) == nil) - startDate = [[NSCalendarDate date] hour:11 minute:0]; - if ((endDate = [_appointment endDate]) == nil) { - endDate = - [startDate hour:[startDate hourOfDay] + 1 minute:0]; - } + [self loadValuesFromComponent: appointment]; uTZ = [[self clientObject] userTimeZone]; - [startDate setTimeZone: uTZ]; + endDate = [appointment endDate]; + if (!endDate) + endDate = [[self startDate] dateByAddingYears: 0 months: 0 days: 0 + hours: 1 minutes: 0 seconds: 0]; + [endDate setTimeZone: uTZ]; -// startDate = [startDate adjustedDate]; -// endDate = [endDate adjustedDate]; - [startDate retain]; [endDate retain]; - - title = [[_appointment summary] copy]; - location = [[_appointment location] copy]; - comment = [[_appointment comment] copy]; - priority = [[_appointment priority] copy]; - categories = [[[_appointment categories] commaSeparatedValues] retain]; - organizer = [[_appointment organizer] retain]; - participants = [[_appointment participants] retain]; - resources = [[_appointment resources] retain]; - -// NSLog (@"summary éàè: '%@'", title); - - s = [_appointment accessClass]; - if(!s || [s isEqualToString:@"PUBLIC"]) - [self setIsPrivate:NO]; - else - [self setIsPrivate:YES]; /* we're possibly loosing information here */ - - /* cycles */ - if ([_appointment isRecurrent]) - { - rrule = [[_appointment recurrenceRules] objectAtIndex: 0]; - [self adjustCycleControlsForRRule:rrule]; - } } - (void)saveValuesIntoAppointment:(iCalEvent *)_appointment { @@ -795,36 +270,10 @@ return appointment; } -/* contact editor compatibility */ - -- (void)setContentString:(NSString *)_s { - [self setICalString:_s]; -} -- (NSString *)contentStringTemplate { - return [self iCalStringTemplate]; -} - -/* access */ - -- (BOOL) isMyApt -{ - // TODO: this should check a set of emails against the SoUser - return (![[organizer email] length] - || [[organizer rfc822Email] isEqualToString: [self emailForUser]]); -} - -- (BOOL)canAccessApt { - return [self isMyApt]; -} - -- (BOOL)canEditApt { - return [self isMyApt]; -} - - /* conflict management */ -- (BOOL)containsConflict:(iCalEvent *)_apt { +- (BOOL) containsConflict: (id) _apt +{ NSArray *attendees, *uids; SOGoAppointmentFolder *groupCalendar; NSArray *infos; @@ -863,28 +312,10 @@ 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 { +- (id) testAction +{ /* for testing only */ WORequest *req; iCalEvent *apt; @@ -901,7 +332,8 @@ return self; } -- (id)defaultAction { +- (id) defaultAction +{ NSString *ical; /* load iCalendar file */ @@ -910,14 +342,14 @@ // ical = [[self clientObject] valueForKey:@"iCalString"]; ical = [[self clientObject] contentAsString]; if ([ical length] == 0) /* a new appointment */ - ical = [self contentStringTemplate]; + ical = [self iCalStringTemplate]; - [self setContentString:ical]; + [self setICalString:ical]; [self loadValuesFromAppointment: [self appointmentFromString: ical]]; - if (![self canEditApt]) { + if (![self canEditComponent]) { /* TODO: we need proper ACLs */ - return [self redirectToLocation:[self _completeURIForMethod:@"../view"]]; + return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; } return self; } @@ -993,17 +425,20 @@ [[self clientObject] baseURL]]; } -- (id)acceptAction { +- (id) acceptAction +{ return [self acceptOrDeclineAction:YES]; } -- (id)declineAction { +- (id) declineAction +{ return [self acceptOrDeclineAction:NO]; } // TODO: add tentatively -- (id)acceptOrDeclineAction:(BOOL)_accept { +- (id) acceptOrDeclineAction: (BOOL) _accept +{ // TODO: this should live in the SoObjects NSException *ex; @@ -1015,7 +450,7 @@ inContext:[self context]]; if (ex != nil) return ex; - return [self redirectToLocation:[self _completeURIForMethod:@"../view"]]; + return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; } @end /* UIxAppointmentEditor */ diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h new file mode 100644 index 000000000..598cfb80c --- /dev/null +++ b/UI/Scheduler/UIxComponentEditor.h @@ -0,0 +1,151 @@ +/* UIxComponentEditor.h - this file is part of SOGo + * + * Copyright (C) 2006 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef UIXCOMPONENTEDITOR_H +#define UIXCOMPONENTEDITOR_H + +#import + +@class NSArray; +@class NSCalendarDate; +@class NSDictionary; +@class NSFormatter; +@class NSString; + +@class iCalPerson; +@class iCalRecurrenceRule; +@class iCalRepeatableEntityObject; + +@interface UIxComponentEditor : UIxComponent +{ +@private + NSString *iCalString; + NSString *errorText; + id item; + + /* individual values */ + NSCalendarDate *startDate; + NSCalendarDate *cycleUntilDate; + 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; +} + +- (void) setAccessClass: (NSString *) _class; +- (NSString *) accessClass; + +- (void) setIsPrivate: (BOOL) _yn; +- (BOOL) isPrivate; + +- (NSArray *) categoryItems; +- (void) setCategories: (NSArray *) _categories; +- (NSArray *) categories; +- (NSString *) itemCategoryText; + +- (NSArray *) priorities; +- (void) setPriority: (NSString *) _priority; +- (NSString *) priority; + +- (void) setItem: (id) _item; +- (id) item; +- (NSString *) itemPriorityText; + +- (void) setErrorText: (NSString *) _txt; +- (NSString *) errorText; +- (BOOL) hasErrorText; + +- (void) setICalString: (NSString *) _s; +- (NSString *) iCalString; + +- (void) setStartDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) startDate; + +- (void) setTitle: (NSString *) _value; +- (NSString *) title; + +- (void) setLocation: (NSString *) _value; +- (NSString *) location; + +- (void) setComment: (NSString *) _value; +- (NSString *) comment; + +- (void) setParticipants: (NSArray *) _parts; +- (NSArray *) participants; + +- (void) setResources: (NSArray *) _res; +- (NSArray *) resources; + +- (void) setCheckForConflicts: (BOOL) _checkForConflicts; +- (BOOL) checkForConflicts; + +- (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; + +/* access */ +- (BOOL) isMyComponent; +- (BOOL) canAccessComponent; +- (BOOL) canEditComponent; + +/* helpers */ +- (NSFormatter *) titleDateFormatter; +- (NSString *) completeURIForMethod: (NSString *) _method; +- (BOOL) isWriteableClientObject; +- (NSException *) validateObjectForStatusChange; + +/* subclasses */ +- (void) loadValuesFromComponent: (iCalRepeatableEntityObject *) component; + +- (NSString *) iCalStringTemplate; +- (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters; +- (NSString *) iCalParticipantsStringFromQueryParameters; +- (NSString *) iCalResourcesStringFromQueryParameters; +- (NSString *) iCalStringFromQueryParameter: (NSString *) _qp + format: (NSString *) _format; +- (NSString *) iCalOrganizerString; + +@end + +#endif /* UIXCOMPONENTEDITOR_H */ diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m new file mode 100644 index 000000000..4469a9ac6 --- /dev/null +++ b/UI/Scheduler/UIxComponentEditor.m @@ -0,0 +1,692 @@ +/* UIxComponentEditor.m - this file is part of SOGo + * + * Copyright (C) 2006 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import "UIxComponent+Agenor.h" + +#import "UIxComponentEditor.h" + +@implementation UIxComponentEditor + +- (id) init +{ + if ((self = [super init])) + { + [self setIsPrivate: NO]; + [self setCheckForConflicts: NO]; + [self setIsCycleEndNever]; + } + + return self; +} + +- (void) dealloc +{ + [iCalString release]; + [errorText release]; + [item release]; + [startDate 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]; + + [super dealloc]; +} + +/* accessors */ + +- (void) setAccessClass: (NSString *) _class +{ + ASSIGN(accessClass, _class); +} + +- (NSString *) accessClass +{ + return accessClass; +} + +- (void) setIsPrivate: (BOOL) _yn +{ + if (_yn) + [self setAccessClass:@"PRIVATE"]; + else + [self setAccessClass:@"PUBLIC"]; + + isPrivate = _yn; +} + +- (BOOL) isPrivate +{ + return isPrivate; +} + +- (void) setItem: (id) _item +{ + ASSIGN(item, _item); +} + +- (id) item +{ + return item; +} + +- (NSString *) itemPriorityText +{ + return [self labelForKey: [NSString stringWithFormat: @"prio_%@", item]]; +} + +- (void) setErrorText: (NSString *) _txt +{ + ASSIGNCOPY(errorText, _txt); +} + +- (NSString *) errorText +{ + return errorText; +} + +- (BOOL) hasErrorText +{ + return [errorText length] > 0 ? YES : NO; +} + +- (void) setStartDate: (NSCalendarDate *) _date +{ + ASSIGN(startDate, _date); +} + +- (NSCalendarDate *) startDate +{ + return startDate; +} + +- (void) setTitle: (NSString *) _value +{ + ASSIGNCOPY(title, _value); +} + +- (NSString *) title +{ + return title; +} + +- (void) setLocation: (NSString *) _value +{ + ASSIGNCOPY(location, _value); +} + +- (NSString *) location +{ + return location; +} + +- (void) setComment: (NSString *) _value +{ + ASSIGNCOPY(comment, _value); +} + +- (NSString *) comment +{ + return comment; +} + +- (NSArray *) categoryItems +{ + // TODO: make this configurable? + /* + Tasks categories will be modified as follow : + – by default (a simple logo or no logo at all), + – task, + – outside, + – meeting, + – holidays, + – phone. + */ + static NSArray *categoryItems = nil; + + if (!categoryItems) + { + categoryItems = [NSArray arrayWithObjects: @"APPOINTMENT", + @"NOT IN OFFICE", + @"MEETING", + @"HOLIDAY", + @"PHONE CALL", + nil]; + [categoryItems retain]; + } + + return categoryItems; +} + +- (void) setCategories: (NSArray *) _categories +{ + ASSIGN(categories, _categories); +} + +- (NSArray *) categories +{ + return categories; +} + +- (NSString *) itemCategoryText +{ + return [[self labelForKey: item] stringByEscapingHTMLString]; +} + +/* priorities */ + +- (NSArray *) priorities +{ + /* 0 == undefined + 5 == normal + 1 == high + */ + static NSArray *priorities = nil; + + if (!priorities) + { + priorities = [NSArray arrayWithObjects:@"0", @"5", @"1", nil]; + [priorities retain]; + } + + return priorities; +} + +- (void) setPriority: (NSString *) _priority +{ + ASSIGN(priority, _priority); +} + +- (NSString *) priority +{ + return priority; +} + +- (void) setParticipants: (NSArray *) _parts +{ + ASSIGN(participants, _parts); +} + +- (NSArray *) participants +{ + return participants; +} + +- (void) setResources: (NSArray *) _res +{ + ASSIGN(resources, _res); +} + +- (NSArray *) resources +{ + return resources; +} + +- (void) setCheckForConflicts: (BOOL) _checkForConflicts +{ + checkForConflicts = _checkForConflicts; +} + +- (BOOL) checkForConflicts +{ + return checkForConflicts; +} + +- (NSArray *) cycles +{ + NSBundle *bundle; + NSString *path; + static NSArray *cycles = nil; + + if (!cycles) + { + 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 +{ + ASSIGN(cycle, _cycle); +} + +- (NSDictionary *) cycle +{ + return cycle; +} + +- (BOOL) hasCycle +{ + return ([cycle objectForKey: @"rule"] != nil); +} + +- (NSString *) cycleLabel +{ + NSString *key; + + key = [(NSDictionary *)item objectForKey:@"label"]; + + return [self labelForKey:key]; +} + +- (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate +{ + NSCalendarDate *until; + + /* copy hour/minute/second from startDate */ + until = [_cycleUntilDate hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute]]; + [until setTimeZone: [startDate timeZone]]; + ASSIGN(cycleUntilDate, until); +} + +- (NSCalendarDate *) cycleUntilDate +{ + return cycleUntilDate; +} + +- (iCalRecurrenceRule *) rrule +{ + NSString *ruleRep; + iCalRecurrenceRule *rule; + + if (![self hasCycle]) + return nil; + ruleRep = [cycle objectForKey:@"rule"]; + rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; + + if (cycleUntilDate && [self isCycleEndUntil]) + [rule setUntilDate:cycleUntilDate]; + + return rule; +} + +- (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule +{ + NSDictionary *c; + NSCalendarDate *until; + + c = [self cycleMatchingRRule:_rrule]; + [self setCycle:c]; + + until = [[[_rrule untilDate] copy] autorelease]; + if (!until) + until = startDate; + else + [self setIsCycleEndUntil]; + + [until setTimeZone:[[self clientObject] userTimeZone]]; + [self setCycleUntilDate:until]; +} + +/* + This method is necessary, because we have a fixed sets of cycles in the UI. + The model is able to represent arbitrary rules, however. + There SHOULD be a different UI, similar to iCal.app, to allow modelling + of more complex rules. + + This method obviously cannot map all existing rules back to the fixed list + in cycles.plist. This should be fixed in a future version when interop + becomes more important. + */ +- (NSDictionary *) cycleMatchingRRule: (iCalRecurrenceRule *) _rrule +{ + NSString *cycleRep; + NSArray *cycles; + unsigned i, count; + + if (!_rrule) + return [[self cycles] objectAtIndex:0]; + + cycleRep = [_rrule versitString]; + cycles = [self cycles]; + count = [cycles count]; + for (i = 1; i < count; i++) { + NSDictionary *c; + NSString *cr; + + c = [cycles objectAtIndex:i]; + cr = [c objectForKey:@"rule"]; + if ([cr isEqualToString:cycleRep]) + return c; + } + [self warnWithFormat:@"No default cycle for rrule found! -> %@", _rrule]; + return nil; +} + +/* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */ +- (NSArray *) cycleEnds +{ + static NSArray *ends = nil; + + if (!ends) + { + ends = [NSArray arrayWithObjects: @"cycle_end_never", + @"cycle_end_until", nil]; + [ends retain]; + } + + return ends; +} + +- (void) setCycleEnd: (NSString *) _cycleEnd +{ + ASSIGNCOPY(cycleEnd, _cycleEnd); +} + +- (NSString *) cycleEnd +{ + return cycleEnd; +} + +- (BOOL) isCycleEndUntil +{ + return (cycleEnd && + [cycleEnd isEqualToString:@"cycle_end_until"]); +} + +- (void) setIsCycleEndUntil +{ + [self setCycleEnd:@"cycle_end_until"]; +} + +- (void) setIsCycleEndNever +{ + [self setCycleEnd:@"cycle_end_never"]; +} + +/* helpers */ +- (NSFormatter *) titleDateFormatter +{ + SOGoDateFormatter *fmt; + + fmt = [[SOGoDateFormatter alloc] initWithLocale: [self locale]]; + [fmt autorelease]; + [fmt setFullWeekdayNameAndDetails]; + + return fmt; +} + +- (NSString *) completeURIForMethod: (NSString *) _method +{ + NSString *uri; + NSRange r; + + uri = [[[self context] request] uri]; + + /* first: identify query parameters */ + r = [uri rangeOfString:@"?" options:NSBackwardsSearch]; + if (r.length > 0) + uri = [uri substringToIndex:r.location]; + + /* next: append trailing slash */ + if (![uri hasSuffix:@"/"]) + uri = [uri stringByAppendingString:@"/"]; + + /* next: append method */ + uri = [uri stringByAppendingString:_method]; + + /* next: append query parameters */ + return [self completeHrefForMethod:uri]; +} + +- (BOOL) isWriteableClientObject +{ + return [[self clientObject] + respondsToSelector:@selector(saveContentString:)]; +} + +- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq + inContext: (WOContext*) _c +{ + return YES; +} + +- (BOOL) containsConflict: (id) _component +{ + [self subclassResponsibility: _cmd]; + + return NO; +} + +/* access */ + +#if 0 +- (iCalPerson *) getOrganizer +{ + iCalPerson *p; + NSString *emailProp; + + emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]]; + p = [[[iCalPerson alloc] init] autorelease]; + [p setEmail:emailProp]; + [p setCn:[self cnForUser]]; + return p; +} +#endif + +- (BOOL) isMyComponent +{ + // TODO: this should check a set of emails against the SoUser + return (![[organizer email] length] + || [[organizer rfc822Email] isEqualToString: [self emailForUser]]); +} + +- (BOOL) canAccessComponent +{ + return [self isMyComponent]; +} + +- (BOOL) canEditComponent +{ + return [self isMyComponent]; +} + +/* response generation */ + +- (NSString *) initialCycleVisibility +{ + return ([self hasCycle] + ? @"visibility: visible;" + : @"visibility: hidden;"); +} + +- (NSString *) initialCycleEndUntilVisibility { + return ([self isCycleEndUntil] + ? @"visibility: visible;" + : @"visibility: hidden;"); +} + +/* subclasses */ +- (void) loadValuesFromComponent: (iCalRepeatableEntityObject *) component +{ + NSString *s; + iCalRecurrenceRule *rrule; + NSTimeZone *uTZ; + + if ((startDate = [component startDate]) == nil) + startDate = [[NSCalendarDate date] hour:11 minute:0]; + + uTZ = [[self clientObject] userTimeZone]; + [startDate setTimeZone: uTZ]; + [startDate retain]; + + title = [[component summary] copy]; + location = [[component location] copy]; + comment = [[component comment] copy]; + priority = [[component priority] copy]; + categories = [[[component categories] commaSeparatedValues] retain]; + organizer = [[component organizer] retain]; + participants = [[component participants] retain]; + resources = [[component resources] retain]; + +// NSLog (@"summary éàè: '%@'", title); + + s = [component accessClass]; + if (!s || [s isEqualToString:@"PUBLIC"]) + [self setIsPrivate:NO]; + else + [self setIsPrivate:YES]; /* we're possibly loosing information here */ + + /* cycles */ + if ([component isRecurrent]) + { + rrule = [[component recurrenceRules] objectAtIndex: 0]; + [self adjustCycleControlsForRRule: rrule]; + } +} + +- (NSString *) iCalStringTemplate +{ + [self subclassResponsibility: _cmd]; + + return @""; +} + +- (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 +{ + return [NSString stringWithFormat: @"ORGANIZER;CN=\"%@\":mailto:%@\r\n", + [self cnForUser], [self emailForUser]]; +} + +- (NSString *) saveUrl +{ + [self subclassResponsibility: _cmd]; + + return @""; +} + +- (NSException *) validateObjectForStatusChange +{ + id co; + + co = [self clientObject]; + if (![co + respondsToSelector: @selector(changeParticipationStatus:inContext:)]) + return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ + reason: + @"method cannot be invoked on the specified object"]; + + return nil; +} + +/* contact editor compatibility */ + +- (void) setICalString: (NSString *) _s +{ + ASSIGNCOPY(iCalString, _s); +} + +- (NSString *) iCalString +{ + return iCalString; +} + +@end diff --git a/UI/Scheduler/UIxTaskEditor.h b/UI/Scheduler/UIxTaskEditor.h index 10ace4390..7509525ee 100644 --- a/UI/Scheduler/UIxTaskEditor.h +++ b/UI/Scheduler/UIxTaskEditor.h @@ -23,68 +23,52 @@ #ifndef UIXTASKEDITOR_H #define UIXTASKEDITOR_H -#import +#import "UIxComponentEditor.h" @class NSString; @class iCalPerson; @class iCalRecurrenceRule; +@class iCalToDo; -@interface UIxTaskEditor : UIxComponent +@interface UIxTaskEditor : UIxComponentEditor { - NSString *iCalString; - NSString *errorText; - id item; - - /* individual values */ - NSCalendarDate *startDate; NSCalendarDate *dueDate; - NSCalendarDate *cycleUntilDate; - 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; } -- (NSString *)iCalStringTemplate; -- (NSString *)iCalString; +- (void) setTaskStartDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) taskStartDate; -- (void)setIsPrivate:(BOOL)_yn; -- (void)setAccessClass:(NSString *)_class; +- (void) setTaskDueDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) taskDueDate; -- (void)setCheckForConflicts:(BOOL)_checkForConflicts; -- (BOOL)checkForConflicts; +/* iCal */ -- (BOOL)hasCycle; -- (iCalRecurrenceRule *)rrule; -- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule; -- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule; +- (NSString *) iCalStringTemplate; -- (BOOL)isCycleEndUntil; -- (void)setIsCycleEndUntil; -- (void)setIsCycleEndNever; +/* new */ -- (NSString *)_completeURIForMethod:(NSString *)_method; +- (id) newAction; -- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values - treatAsResource:(BOOL)_isResource; +/* save */ -- (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters; -- (NSString *)iCalParticipantsStringFromQueryParameters; -- (NSString *)iCalResourcesStringFromQueryParameters; -- (NSString *)iCalStringFromQueryParameter:(NSString *)_qp - format:(NSString *)_format; -- (NSString *)iCalOrganizerString; +- (void) loadValuesFromTask: (iCalToDo *) _task; +- (void) saveValuesIntoTask: (iCalToDo *) _task; +- (iCalToDo *) taskFromString: (NSString *) _iCalString; -- (id)acceptOrDeclineAction:(BOOL)_accept; +/* conflict management */ + +- (BOOL) containsConflict: (id) _task; +- (id ) defaultAction; +- (id ) saveAction; +- (id) changeStatusAction; +- (id) acceptAction; +- (id) declineAction; + +- (NSString *) saveUrl; + +// TODO: add tentatively + +- (id) acceptOrDeclineAction: (BOOL) _accept; @end diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 3dda7f162..052224a6e 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -19,9 +19,6 @@ 02111-1307, USA. */ -#import -#import - #import #import "UIxTaskEditor.h" @@ -39,382 +36,36 @@ @implementation UIxTaskEditor -+ (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 { - [iCalString release]; - [errorText release]; - [item release]; - - [startDate release]; +- (void) dealloc +{ [dueDate 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]; [super dealloc]; } -/* accessors */ - -- (void) setItem: (id) _item +- (void) setTaskStartDate: (NSCalendarDate *) _date { - ASSIGN(item, _item); + [self setStartDate: _date]; } -- (id) item +- (NSCalendarDate *) taskStartDate { - return item; + return [self startDate]; } -- (void)setErrorText:(NSString *)_txt { - ASSIGNCOPY(errorText, _txt); -} -- (NSString *)errorText { - return errorText; -} -- (BOOL)hasErrorText { - return [errorText length] > 0 ? YES : NO; -} - -- (NSFormatter *)titleDateFormatter { - SOGoDateFormatter *fmt; - - fmt = [[[SOGoDateFormatter alloc] initWithLocale:[self locale]] autorelease]; - [fmt setFullWeekdayNameAndDetails]; - return fmt; -} - -- (void)setTaskStartDate:(NSCalendarDate *)_date { - ASSIGN(startDate, _date); -} -- (NSCalendarDate *)taskStartDate { - return startDate; -} -- (void)setTaskDueDate:(NSCalendarDate *)_date { +- (void) setTaskDueDate: (NSCalendarDate *) _date +{ ASSIGN(dueDate, _date); } -- (NSCalendarDate *)taskDueDate { + +- (NSCalendarDate *) taskDueDate +{ return dueDate; } -- (void)setTitle:(NSString *)_value { - ASSIGNCOPY(title, _value); -} -- (NSString *)title { - return title; -} -- (void)setLocation:(NSString *)_value { - ASSIGNCOPY(location, _value); -} -- (NSString *)location { - return location; -} -- (void)setComment:(NSString *)_value { - ASSIGNCOPY(comment, _value); -} -- (NSString *)comment { - return comment; -} - -- (void)setParticipants:(NSArray *)_parts { - ASSIGN(participants, _parts); -} -- (NSArray *)participants { - return participants; -} -- (void)setResources:(NSArray *)_res { - ASSIGN(resources, _res); -} -- (NSArray *)resources { - return resources; -} - -/* 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; - - key = [NSString stringWithFormat:@"prio_%@", item]; - return [self labelForKey:key]; -} - -- (void)setPriority:(NSString *)_priority { - ASSIGN(priority, _priority); -} -- (NSString *)priority { - return priority; -} - - -/* categories */ - -- (NSArray *)categoryItems { - // TODO: make this configurable? - /* - Tasks categories will be modified as follow : - – by default (a simple logo or no logo at all), - – task, - – outside, - – meeting, - – holidays, - – phone. - */ - static NSArray *categoryItems = nil; - - if (!categoryItems) { - categoryItems = [[NSArray arrayWithObjects:@"TASK", - @"NOT IN OFFICE", - @"MEETING", - @"HOLIDAY", - @"PHONE CALL", - nil] retain]; - } - return categoryItems; -} - -- (NSString *) itemCategoryText { - return [[self labelForKey: item] stringByEscapingHTMLString]; -} - -- (void)setCategories:(NSArray *)_categories { - ASSIGN(categories, _categories); -} - -- (NSArray *)categories { - return categories; -} - -/* 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 { - ASSIGN(accessClass, _class); -} -- (NSString *)accessClass { - return accessClass; -} - -- (void)setIsPrivate:(BOOL)_yn { - if (_yn) - [self setAccessClass:@"PRIVATE"]; - else - [self setAccessClass:@"PUBLIC"]; - isPrivate = _yn; -} -- (BOOL)isPrivate { - return isPrivate; -} - -- (void)setCheckForConflicts:(BOOL)_checkForConflicts { - checkForConflicts = _checkForConflicts; -} -- (BOOL)checkForConflicts { - return checkForConflicts; -} - -- (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 { - ASSIGN(cycle, _cycle); -} -- (NSDictionary *)cycle { - return cycle; -} -- (BOOL)hasCycle { - [self debugWithFormat:@"cycle: %@", cycle]; - if (![cycle objectForKey:@"rule"]) - return NO; - return YES; -} -- (NSString *)cycleLabel { - NSString *key; - - key = [(NSDictionary *)item objectForKey:@"label"]; - return [self labelForKey:key]; -} - - -- (void)setCycleUntilDate:(NSCalendarDate *)_cycleUntilDate { - NSCalendarDate *until; - - /* copy hour/minute/second from startDate */ - until = [_cycleUntilDate hour:[startDate hourOfDay] - minute:[startDate minuteOfHour] - second:[startDate secondOfMinute]]; - [until setTimeZone:[startDate timeZone]]; - ASSIGN(cycleUntilDate, until); -} -- (NSCalendarDate *)cycleUntilDate { - return cycleUntilDate; -} - -- (iCalRecurrenceRule *)rrule { - NSString *ruleRep; - iCalRecurrenceRule *rule; - - if (![self hasCycle]) - return nil; - ruleRep = [cycle objectForKey:@"rule"]; - rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; - - if (cycleUntilDate && [self isCycleEndUntil]) - [rule setUntilDate:cycleUntilDate]; - return rule; -} - -- (void)adjustCycleControlsForRRule:(iCalRecurrenceRule *)_rrule { - NSDictionary *c; - NSCalendarDate *until; - - c = [self cycleMatchingRRule:_rrule]; - [self setCycle:c]; - - until = [[[_rrule untilDate] copy] autorelease]; - if (!until) - until = startDate; - else - [self setIsCycleEndUntil]; - - [until setTimeZone:[[self clientObject] userTimeZone]]; - [self setCycleUntilDate:until]; -} - -/* - This method is necessary, because we have a fixed sets of cycles in the UI. - The model is able to represent arbitrary rules, however. - There SHOULD be a different UI, similar to iCal.app, to allow modelling - of more complex rules. - - This method obviously cannot map all existing rules back to the fixed list - in cycles.plist. This should be fixed in a future version when interop - becomes more important. - */ -- (NSDictionary *)cycleMatchingRRule:(iCalRecurrenceRule *)_rrule { - NSString *cycleRep; - NSArray *cycles; - unsigned i, count; - - if (!_rrule) - return [[self cycles] objectAtIndex:0]; - - cycleRep = [_rrule versitString]; - cycles = [self cycles]; - count = [cycles count]; - for (i = 1; i < count; i++) { - NSDictionary *c; - NSString *cr; - - c = [cycles objectAtIndex:i]; - cr = [c objectForKey:@"rule"]; - if ([cr isEqualToString:cycleRep]) - return c; - } - [self warnWithFormat:@"No default cycle for rrule found! -> %@", _rrule]; - return nil; -} - -/* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */ -- (NSArray *)cycleEnds { - static NSArray *ends = nil; - - if (!ends) { - ends = [[NSArray alloc] initWithObjects:@"cycle_end_never", - @"cycle_end_until", - nil]; - } - return ends; -} - -- (void)setCycleEnd:(NSString *)_cycleEnd { - ASSIGNCOPY(cycleEnd, _cycleEnd); -} -- (NSString *)cycleEnd { - return cycleEnd; -} -- (BOOL)isCycleEndUntil { - return (cycleEnd && - [cycleEnd isEqualToString:@"cycle_end_until"]); -} -- (void)setIsCycleEndUntil { - [self setCycleEnd:@"cycle_end_until"]; -} -- (void)setIsCycleEndNever { - [self setCycleEnd:@"cycle_end_never"]; -} - /* iCal */ -- (void)setICalString:(NSString *)_s { - ASSIGNCOPY(iCalString, _s); -} -- (NSString *)iCalString { - return iCalString; -} - -- (NSString *)iCalStringTemplate { +- (NSString *) iCalStringTemplate +{ static NSString *iCalStringTemplate = \ @"BEGIN:VCALENDAR\r\n" @"METHOD:REQUEST\r\n" @@ -464,105 +115,10 @@ 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; - NSString *emailProp; - - 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 { +- (id) newAction +{ /* This method creates a unique ID and redirects to the "edit" method on the new ID. @@ -618,120 +174,24 @@ /* save */ -/* returned dates are in GMT */ -- (NSArray *) getICalPersonsFromFormValues: (NSArray *) _values - treatAsResource: (BOOL) _isResource +- (void) loadValuesFromTask: (iCalToDo *) _task { - unsigned i, count; - NSMutableArray *result; - - count = [_values count]; - result = [[NSMutableArray alloc] initWithCapacity:count]; - for (i = 0; i < count; i++) { - NSString *pString, *email, *cn; - NSRange r; - 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; -} - -- (void)loadValuesFromTask: (iCalToDo *)_task -{ - NSString *s; - iCalRecurrenceRule *rrule; NSTimeZone *uTZ; - if ((startDate = [_task startDate]) == nil) - startDate = [[NSCalendarDate date] hour:11 minute:0]; - if ((dueDate = [_task due]) == nil) { - dueDate = - [startDate hour:[startDate hourOfDay] + 1 minute:0]; - } + [self loadValuesFromComponent: _task]; uTZ = [[self clientObject] userTimeZone]; - [startDate setTimeZone: uTZ]; + dueDate = [_task due]; + if (!dueDate) + dueDate = [[self startDate] dateByAddingYears: 0 months: 0 days: 0 + hours: 1 minutes: 0 seconds: 0]; + [dueDate setTimeZone: uTZ]; -// startDate = [startDate adjustedDate]; -// dueDate = [dueDate adjustedDate]; - [startDate retain]; [dueDate retain]; - - title = [[_task summary] copy]; - location = [[_task location] copy]; - comment = [[_task comment] copy]; - priority = [[_task priority] copy]; - categories = [[[_task categories] commaSeparatedValues] retain]; - organizer = [[_task organizer] retain]; - participants = [[_task participants] retain]; - resources = [[_task resources] retain]; - -// NSLog (@"summary éàè: '%@'", title); - - s = [_task accessClass]; - if(!s || [s isEqualToString:@"PUBLIC"]) - [self setIsPrivate:NO]; - else - [self setIsPrivate:YES]; /* we're possibly loosing information here */ - - /* cycles */ - if ([_task isRecurrent]) - { - rrule = [[_task recurrenceRules] objectAtIndex: 0]; - [self adjustCycleControlsForRRule:rrule]; - } } -- (void)saveValuesIntoTask:(iCalToDo *)_task { +- (void) saveValuesIntoTask: (iCalToDo *) _task +{ /* merge in form values */ NSArray *attendees, *lResources; iCalRecurrenceRule *rrule; @@ -785,36 +245,10 @@ return task; } -/* contact editor compatibility */ - -- (void)setContentString:(NSString *)_s { - [self setICalString:_s]; -} -- (NSString *)contentStringTemplate { - return [self iCalStringTemplate]; -} - -/* access */ - -- (BOOL) isMyTask -{ - // TODO: this should check a set of emails against the SoUser - return (![[organizer email] length] - || [[organizer rfc822Email] isEqualToString: [self emailForUser]]); -} - -- (BOOL)canAccessTask { - return [self isMyTask]; -} - -- (BOOL)canEditTask { - return [self isMyTask]; -} - - /* conflict management */ -- (BOOL)containsConflict:(iCalToDo *)_task { +- (BOOL) containsConflict: (id) _task +{ NSArray *attendees, *uids; SOGoAppointmentFolder *groupCalendar; NSArray *infos; @@ -853,47 +287,6 @@ 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 */ - WORequest *req; - iCalToDo *task; - NSString *content; - - req = [[self context] request]; - task = [self taskFromString: [self iCalString]]; - [self saveValuesIntoTask:task]; - content = [[task parent] versitString]; - [self logWithFormat:@"%s -- iCal:\n%@", - __PRETTY_FUNCTION__, - content]; - - return self; -} - - (id ) defaultAction { NSString *ical; @@ -904,14 +297,14 @@ // ical = [[self clientObject] valueForKey:@"iCalString"]; ical = [[self clientObject] contentAsString]; if ([ical length] == 0) /* a new task */ - ical = [self contentStringTemplate]; + ical = [self iCalStringTemplate]; - [self setContentString:ical]; + [self setICalString:ical]; [self loadValuesFromTask: [self taskFromString: ical]]; - if (![self canEditTask]) { + if (![self canEditComponent]) { /* TODO: we need proper ACLs */ - return [self redirectToLocation:[self _completeURIForMethod:@"../view"]]; + return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; } return self; @@ -1017,7 +410,7 @@ return self; } - return [self redirectToLocation: [self _completeURIForMethod: @".."]]; + return [self redirectToLocation: [self completeURIForMethod: @".."]]; } - (id)acceptAction { @@ -1048,7 +441,7 @@ inContext:[self context]]; if (ex != nil) return ex; - return [self redirectToLocation:[self _completeURIForMethod:@"../view"]]; + return [self redirectToLocation: [self completeURIForMethod:@"../view"]]; } @end /* UIxTaskEditor */