2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 #include <SOGoUI/UIxComponent.h>
26 #include <SOGoLogic/SOGoAppointment.h>
27 #include <NGiCal/NGiCal.h>
30 /* TODO: CLEAN THIS MESS UP */
32 @interface NSDate(UsedPrivates)
33 - (NSString *)icalString; // TODO: this is in NGiCal
36 @interface NSObject (AppointmentHack)
37 - (BOOL)isAppointment;
40 @implementation NSObject (AppointmentHack)
41 - (BOOL)isAppointment {
42 return [self isKindOfClass:NSClassFromString(@"SOGoAppointmentObject")];
46 @interface iCalPerson (Convenience)
47 - (NSString *)rfc822Email;
50 @implementation iCalPerson (Convenience)
51 - (NSString *)rfc822Email {
52 NSString *_email = [self email];
53 NSRange colon = [_email rangeOfString:@":"];
54 if(colon.location != NSNotFound) {
55 return [_email substringFromIndex:colon.location + 1];
61 @interface UIxAppointmentEditor : UIxComponent
67 - (SOGoAppointment *)appointment;
68 - (NSString *)iCalStringTemplate;
69 - (NSString *)iCalString;
70 - (BOOL)isNewAppointment;
74 @implementation UIxAppointmentEditor
77 [self->appointment release];
78 [self->participants release];
86 - (NSString *)formattedAptStartTime {
89 date = [[self appointment] startDate];
90 /* TODO: convert this into display timeZone! */
91 return [date descriptionWithCalendarFormat:@"%A, %Y-%m-%d %H:%M %Z"];
94 - (BOOL)isNewAppointment {
95 /* that doesn't work! */
96 return ![[self clientObject] isAppointment];
99 - (NSString *)iCalString {
100 // TODO: improve check for new appointment
103 ical = [[self clientObject] valueForKey:@"iCalString"];
104 if ([ical length] == 0) /* a new appointment */
105 ical = [self iCalStringTemplate];
110 - (NSString *)iCalStringTemplate {
111 static NSString *iCalStringTemplate = \
112 @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
117 @"STATUS:CONFIRMED\n"
125 NSCalendarDate *startDate, *endDate;
128 startDate = [self selectedDate];
129 endDate = [startDate dateByAddingYears:0 months:0 days:0
130 hours:1 minutes:0 seconds:0];
132 template = [NSString stringWithFormat:iCalStringTemplate,
133 [[self clientObject] nameInContainer],
134 [[NSCalendarDate date] icalString],
135 [startDate icalString],
136 [endDate icalString]];
144 - (SOGoAppointment *)appointment {
145 if(self->appointment == nil) {
146 self->appointment = [[SOGoAppointment alloc]
147 initWithICalString:[self iCalString]];
149 return self->appointment;
154 NSMutableArray *emailAddresses;
157 if (self->participants)
158 return self->participants;
160 attendees = [self->appointment attendees];
161 count = [attendees count];
162 emailAddresses = [[NSMutableArray alloc] initWithCapacity:count];
163 for (i = 0; i < count; i++) {
166 email = [[attendees objectAtIndex:i] rfc822Email];
168 [emailAddresses addObject:email];
172 [[emailAddresses componentsJoinedByString:@"\n"] copy];
173 [emailAddresses release];
174 return self->participants;
180 - (NSString *)uriAsFormat {
184 uri = [[[self context] request] uri];
186 /* first: identify query parameters */
187 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
189 uri = [uri substringToIndex:r.location];
190 qp = [uri substringFromIndex:r.location];
195 /* next: strip trailing slash */
196 if ([uri hasSuffix:@"/"])
197 uri = [uri substringToIndex:([uri length] - 1)];
198 r = [uri rangeOfString:@"/" options:NSBackwardsSearch];
200 /* then: cut of last path component */
201 if (r.length == 0) // no slash? are we at root?
204 uri = [uri substringToIndex:(r.location + 1)];
206 /* next: append format token */
207 uri = [uri stringByAppendingString:@"%@"];
209 uri = [uri stringByAppendingString:qp];
219 This method creates a unique ID and redirects to the "edit" method on the
221 It is actually a folder method and should be defined on the folder.
223 Note: 'clientObject' is the SOGoAppointmentFolder!
227 NSString *uri, *uriFormat, *objectId, *nextMethod;
229 objectId = [NSClassFromString(@"SOGoAppointmentFolder")
230 globallyUniqueObjectId];
232 nextMethod = [NSString stringWithFormat:@"%@/edit", objectId];
233 uriFormat = [self uriAsFormat];
234 uri = [[NSString alloc] initWithFormat:uriFormat, nextMethod];
236 req = [[self context] request];
237 r = [WOResponse responseWithRequest:req];
238 [r setStatus:302 /* moved */];
239 [r setHeader:uri forKey:@"location"];
240 [uri release]; uri = nil;
247 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
248 _str = [_str stringByAppendingString:@" GMT"];
249 return [NSCalendarDate dateWithString:_str
250 calendarFormat:@"%Y-%m-%d %H:%M %Z"];
254 SOGoAppointment *apt;
255 NSString *iCalString;
256 NSString *summary, *location, *nextMethod, *uri, *uriFormat;
257 NSCalendarDate *sd, *ed;
263 req = [[self context] request];
265 /* get iCalString from hidden input */
267 iCalString = [req formValueForKey:@"ical"];
268 if ([iCalString length] == 0) {
269 // TODO: improve user experience ... (eg error panel like Zope)
270 return [NSException exceptionWithHTTPStatus:400 /* bad request */
271 reason:@"missing iCalendar form data in request"];
274 apt = [[SOGoAppointment alloc] initWithICalString:iCalString];
276 /* merge in form values */
278 sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
279 ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
280 [apt setStartDate:sd];
283 summary = [req formValueForKey:@"summary"];
284 [apt setSummary:summary];
285 location = [req formValueForKey:@"location"];
286 [apt setLocation:location];
288 [apt removeAllAttendees]; /* clean up */
289 ps = [[req formValueForKey:@"participants"]
290 componentsSeparatedByString:@"\n"];
292 for (i = 0; i < count; i++) {
297 email = [ps objectAtIndex:i];
298 if ([email length] == 0)
301 p = [[iCalPerson alloc] init];
302 [p setEmail:[@"mailto:" stringByAppendingString:[email stringValue]]];
303 /* construct a fake CN */
304 cnr = [email rangeOfString:@"@"];
306 [p setCn:[email substringToIndex:cnr.location]];
307 [apt addToAttendees:p];
311 /* receive current representation for save operation */
312 iCalString = [apt iCalString];
313 [apt release]; apt = nil;
315 /* determine what's to do and where to go next */
316 if([self isNewAppointment])
317 nextMethod = @"duhduh";
319 nextMethod = @"view";
322 NSLog(@"%s new iCalString:\n%@", __PRETTY_FUNCTION__, iCalString);
327 ex = [[self clientObject] saveContentString:iCalString];
329 // TODO: add some error handling in form! (eg like in Zope)
333 uriFormat = [self uriAsFormat];
334 uri = [NSString stringWithFormat:uriFormat, nextMethod];
336 r = [WOResponse responseWithRequest:req];
337 [r setStatus:302 /* moved */];
338 [r setHeader:uri forKey:@"location"];
342 @end /* UIxAppointmentEditor */