/* Copyright (C) 2000-2004 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. */ // $Id$ #include "common.h" #include #include #include #include /* TODO: CLEAN THIS MESS UP */ @interface NSDate(UsedPrivates) - (NSString *)icalString; // TODO: this is in NGiCal @end @interface NSObject (AppointmentHack) - (BOOL)isAppointment; @end @implementation NSObject (AppointmentHack) - (BOOL)isAppointment { return [self isKindOfClass:NSClassFromString(@"SOGoAppointmentObject")]; } @end @interface iCalPerson (Convenience) - (NSString *)rfc822Email; @end @implementation iCalPerson (Convenience) - (NSString *)rfc822Email { NSString *_email = [self email]; NSRange colon = [_email rangeOfString:@":"]; if(colon.location != NSNotFound) { return [_email substringFromIndex:colon.location + 1]; } return _email; } @end @interface UIxAppointmentEditor : UIxComponent { id appointment; id participants; } - (SOGoAppointment *)appointment; - (NSString *)iCalStringTemplate; - (NSString *)iCalString; @end @implementation UIxAppointmentEditor - (void)dealloc { [self->appointment release]; [self->participants release]; [super dealloc]; } /* accessors */ - (NSString *)formattedAptStartTime { NSCalendarDate *date; SOGoDateFormatter *fmt; NSString *s; date = [[[[self appointment] startDate] copy] autorelease]; [date setTimeZone:[self viewTimeZone]]; fmt = [[SOGoDateFormatter alloc] initWithLocale:[self locale]]; [fmt setFullWeekdayNameAndDetails]; s = [fmt stringForObjectValue:date]; [fmt release]; return s; } - (BOOL)isNewAppointment { /* that doesn't work! */ return ![[self clientObject] isAppointment]; } - (NSString *)iCalString { // TODO: improve check for new appointment NSString *ical; ical = [[self clientObject] valueForKey:@"iCalString"]; if ([ical length] == 0) /* a new appointment */ ical = [self iCalStringTemplate]; return ical; } - (NSString *)iCalStringTemplate { static NSString *iCalStringTemplate = \ @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n" @"VERSION:2.0\n" @"BEGIN:VEVENT\n" @"UID:%@\n" @"CLASS:PRIVATE\n" @"STATUS:CONFIRMED\n" @"DTSTAMP:%@\n" @"DTSTART:%@\n" @"DTEND:%@\n" @"TRANSP:OPAQUE\n" @"SEQUENCE:1\n" @"END:VEVENT\n" @"END:VCALENDAR"; NSCalendarDate *startDate, *endDate; NSString *template; startDate = [self selectedDate]; endDate = [startDate dateByAddingYears:0 months:0 days:0 hours:1 minutes:0 seconds:0]; template = [NSString stringWithFormat:iCalStringTemplate, [[self clientObject] nameInContainer], [[NSCalendarDate date] icalString], [startDate icalString], [endDate icalString]]; return template; } /* backend */ - (SOGoAppointment *)appointment { if(self->appointment == nil) { self->appointment = [[SOGoAppointment alloc] initWithICalString:[self iCalString]]; } return self->appointment; } - (id)participants { NSArray *attendees; NSMutableArray *emailAddresses; unsigned i, count; if (self->participants) return self->participants; attendees = [self->appointment attendees]; count = [attendees count]; emailAddresses = [[NSMutableArray alloc] initWithCapacity:count]; for (i = 0; i < count; i++) { NSString *email; email = [[attendees objectAtIndex:i] rfc822Email]; if (email) [emailAddresses addObject:email]; } self->participants = [[emailAddresses componentsJoinedByString:@"\n"] copy]; [emailAddresses release]; return self->participants; } /* JavaScript */ - (NSString *)jsAddParticipant { static NSString *script = \ @"function addParticipant(cn, email) {" @" var table = document.getElementById('participants');" @" var tr = document.createElement('tr');" @" var td, checkbox, text;" @"" @" td = document.createElement('td');" @" checkbox = document.createElement('input');" @" checkbox.setAttribute('type', 'checkbox');" @" checkbox.setAttribute('checked', 'checked');" @" checkbox.setAttribute('value', '1');" @" checkbox.setAttribute('id', email);" @" checkbox.setAttribute('name', email);" @" td.appendChild(checkbox);" @" tr.appendChild(td);" @" td = document.createElement('td');" @" text = document.createTextNode(cn);" @" td.appendChild(text);" @" tr.appendChild(td);" @" table.appendChild(tr);" @"}\n"; return script; } /* helper */ - (NSString *)uriAsFormat { NSString *uri, *qp; NSRange r; uri = [[[self context] request] uri]; /* first: identify query parameters */ r = [uri rangeOfString:@"?" options:NSBackwardsSearch]; if (r.length > 0) { qp = [uri substringFromIndex:r.location]; uri = [uri substringToIndex:r.location]; } else qp = nil; /* next: strip trailing slash */ if ([uri hasSuffix:@"/"]) uri = [uri substringToIndex:([uri length] - 1)]; r = [uri rangeOfString:@"/" options:NSBackwardsSearch]; /* then: cut of last path component */ if (r.length == 0) // no slash? are we at root? uri = @"/"; else uri = [uri substringToIndex:(r.location + 1)]; /* next: append format token */ uri = [uri stringByAppendingString:@"%@"]; if(qp != nil) uri = [uri stringByAppendingString:qp]; return 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! */ WORequest *req; WOResponse *r; NSString *uri, *uriFormat, *objectId, *nextMethod; objectId = [NSClassFromString(@"SOGoAppointmentFolder") globallyUniqueObjectId]; nextMethod = [NSString stringWithFormat:@"%@/edit", objectId]; uriFormat = [self uriAsFormat]; uri = [[NSString alloc] initWithFormat:uriFormat, nextMethod]; req = [[self context] request]; r = [WOResponse responseWithRequest:req]; [r setStatus:302 /* moved */]; [r setHeader:uri forKey:@"location"]; [uri release]; uri = nil; return r; } /* save */ /* returned dates are in GMT */ - (NSCalendarDate *)_dateFromString:(NSString *)_str { NSCalendarDate *date; date = [NSCalendarDate dateWithString:_str calendarFormat:@"%Y-%m-%d %H:%M %Z"]; [date setTimeZone:[self backendTimeZone]]; return date; } - (id)saveAction { SOGoAppointment *apt; NSString *iCalString; NSString *summary, *location, *uri, *uriFormat; NSCalendarDate *sd, *ed; NSArray *ps; unsigned i, count; WOResponse *r; WORequest *req; req = [[self context] request]; /* get iCalString from hidden input */ iCalString = [req formValueForKey:@"ical"]; if ([iCalString length] == 0) { // TODO: improve user experience ... (eg error panel like Zope) return [NSException exceptionWithHTTPStatus:400 /* bad request */ reason:@"missing iCalendar form data in request"]; } apt = [[SOGoAppointment alloc] initWithICalString:iCalString]; /* merge in form values */ sd = [self _dateFromString:[req formValueForKey:@"startDate"]]; ed = [self _dateFromString:[req formValueForKey:@"endDate"]]; [apt setStartDate:sd]; [apt setEndDate:ed]; summary = [req formValueForKey:@"summary"]; [apt setSummary:summary]; location = [req formValueForKey:@"location"]; [apt setLocation:location]; [apt removeAllAttendees]; /* clean up */ ps = [[req formValueForKey:@"participants"] componentsSeparatedByString:@"\n"]; count = [ps count]; for (i = 0; i < count; i++) { NSString *email; iCalPerson *p; NSRange cnr; email = [ps objectAtIndex:i]; if ([email length] == 0) continue; p = [[iCalPerson alloc] init]; [p setEmail:[@"mailto:" stringByAppendingString:[email stringValue]]]; /* construct a fake CN */ cnr = [email rangeOfString:@"@"]; if (cnr.length > 0) [p setCn:[email substringToIndex:cnr.location]]; [apt addToAttendees:p]; [p release]; } /* receive current representation for save operation */ iCalString = [apt iCalString]; [apt release]; apt = nil; #if 0 NSLog(@"%s new iCalString:\n%@", __PRETTY_FUNCTION__, iCalString); #else { NSException *ex; ex = [[self clientObject] saveContentString:iCalString]; if (ex) return ex; // TODO: add some error handling in form! (eg like in Zope) } #endif uriFormat = [self uriAsFormat]; uri = [NSString stringWithFormat:uriFormat, @"view"]; r = [WOResponse responseWithRequest:req]; [r setStatus:302 /* moved */]; [r setHeader:uri forKey:@"location"]; return r; } @end /* UIxAppointmentEditor */