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 <SOGoUI/SOGoDateFormatter.h>
27 #include <SOGoLogic/SOGoAppointment.h>
28 #include <NGiCal/NGiCal.h>
31 /* TODO: CLEAN THIS MESS UP */
33 @interface NSDate(UsedPrivates)
34 - (NSString *)icalString; // TODO: this is in NGiCal
37 @interface NSObject (AppointmentHack)
38 - (BOOL)isAppointment;
41 @implementation NSObject (AppointmentHack)
42 - (BOOL)isAppointment {
43 return [self isKindOfClass:NSClassFromString(@"SOGoAppointmentObject")];
47 @interface iCalPerson (Convenience)
48 - (NSString *)rfc822Email;
51 @implementation iCalPerson (Convenience)
52 - (NSString *)rfc822Email {
53 NSString *_email = [self email];
54 NSRange colon = [_email rangeOfString:@":"];
55 if(colon.location != NSNotFound) {
56 return [_email substringFromIndex:colon.location + 1];
62 @interface UIxAppointmentEditor : UIxComponent
68 - (SOGoAppointment *)appointment;
69 - (NSString *)iCalStringTemplate;
70 - (NSString *)iCalString;
74 @implementation UIxAppointmentEditor
77 [self->appointment release];
78 [self->participants release];
86 - (NSString *)formattedAptStartTime {
88 SOGoDateFormatter *fmt;
91 date = [[[[self appointment] startDate] copy] autorelease];
92 [date setTimeZone:[self viewTimeZone]];
93 fmt = [[SOGoDateFormatter alloc] initWithLocale:[self locale]];
94 [fmt setFullWeekdayNameAndDetails];
95 s = [fmt stringForObjectValue:date];
100 - (BOOL)isNewAppointment {
101 /* that doesn't work! */
102 return ![[self clientObject] isAppointment];
105 - (NSString *)iCalString {
106 // TODO: improve check for new appointment
109 ical = [[self clientObject] valueForKey:@"iCalString"];
110 if ([ical length] == 0) /* a new appointment */
111 ical = [self iCalStringTemplate];
116 - (NSString *)iCalStringTemplate {
117 static NSString *iCalStringTemplate = \
118 @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
123 @"STATUS:CONFIRMED\n"
131 NSCalendarDate *startDate, *endDate;
134 startDate = [self selectedDate];
135 endDate = [startDate dateByAddingYears:0 months:0 days:0
136 hours:1 minutes:0 seconds:0];
138 template = [NSString stringWithFormat:iCalStringTemplate,
139 [[self clientObject] nameInContainer],
140 [[NSCalendarDate date] icalString],
141 [startDate icalString],
142 [endDate icalString]];
150 - (SOGoAppointment *)appointment {
151 if(self->appointment == nil) {
152 self->appointment = [[SOGoAppointment alloc]
153 initWithICalString:[self iCalString]];
155 return self->appointment;
160 NSMutableArray *emailAddresses;
163 if (self->participants)
164 return self->participants;
166 attendees = [self->appointment attendees];
167 count = [attendees count];
168 emailAddresses = [[NSMutableArray alloc] initWithCapacity:count];
169 for (i = 0; i < count; i++) {
172 email = [[attendees objectAtIndex:i] rfc822Email];
174 [emailAddresses addObject:email];
178 [[emailAddresses componentsJoinedByString:@"\n"] copy];
179 [emailAddresses release];
180 return self->participants;
186 - (NSString *)uriAsFormat {
190 uri = [[[self context] request] uri];
192 /* first: identify query parameters */
193 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
195 qp = [uri substringFromIndex:r.location];
196 uri = [uri substringToIndex:r.location];
201 /* next: strip trailing slash */
202 if ([uri hasSuffix:@"/"])
203 uri = [uri substringToIndex:([uri length] - 1)];
204 r = [uri rangeOfString:@"/" options:NSBackwardsSearch];
206 /* then: cut of last path component */
207 if (r.length == 0) // no slash? are we at root?
210 uri = [uri substringToIndex:(r.location + 1)];
212 /* next: append format token */
213 uri = [uri stringByAppendingString:@"%@"];
215 uri = [uri stringByAppendingString:qp];
225 This method creates a unique ID and redirects to the "edit" method on the
227 It is actually a folder method and should be defined on the folder.
229 Note: 'clientObject' is the SOGoAppointmentFolder!
233 NSString *uri, *uriFormat, *objectId, *nextMethod;
235 objectId = [NSClassFromString(@"SOGoAppointmentFolder")
236 globallyUniqueObjectId];
238 nextMethod = [NSString stringWithFormat:@"%@/edit", objectId];
239 uriFormat = [self uriAsFormat];
240 uri = [[NSString alloc] initWithFormat:uriFormat, nextMethod];
241 req = [[self context] request];
242 r = [WOResponse responseWithRequest:req];
243 [r setStatus:302 /* moved */];
244 [r setHeader:uri forKey:@"location"];
245 [uri release]; uri = nil;
252 /* returned dates are in GMT */
253 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
254 NSCalendarDate *date;
256 date = [NSCalendarDate dateWithString:_str
257 calendarFormat:@"%Y-%m-%d %H:%M %Z"];
258 [date setTimeZone:[self backendTimeZone]];
263 SOGoAppointment *apt;
264 NSString *iCalString;
265 NSString *summary, *location, *uri, *uriFormat;
266 NSCalendarDate *sd, *ed;
272 req = [[self context] request];
274 /* get iCalString from hidden input */
276 iCalString = [req formValueForKey:@"ical"];
277 if ([iCalString length] == 0) {
278 // TODO: improve user experience ... (eg error panel like Zope)
279 return [NSException exceptionWithHTTPStatus:400 /* bad request */
280 reason:@"missing iCalendar form data in request"];
283 apt = [[SOGoAppointment alloc] initWithICalString:iCalString];
285 /* merge in form values */
287 sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
288 ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
289 [apt setStartDate:sd];
292 summary = [req formValueForKey:@"summary"];
293 [apt setSummary:summary];
294 location = [req formValueForKey:@"location"];
295 [apt setLocation:location];
297 [apt removeAllAttendees]; /* clean up */
298 ps = [[req formValueForKey:@"participants"]
299 componentsSeparatedByString:@"\n"];
301 for (i = 0; i < count; i++) {
306 email = [ps objectAtIndex:i];
307 if ([email length] == 0)
310 p = [[iCalPerson alloc] init];
311 [p setEmail:[@"mailto:" stringByAppendingString:[email stringValue]]];
312 /* construct a fake CN */
313 cnr = [email rangeOfString:@"@"];
315 [p setCn:[email substringToIndex:cnr.location]];
316 [apt addToAttendees:p];
320 /* receive current representation for save operation */
321 iCalString = [apt iCalString];
322 [apt release]; apt = nil;
325 NSLog(@"%s new iCalString:\n%@", __PRETTY_FUNCTION__, iCalString);
330 ex = [[self clientObject] saveContentString:iCalString];
332 // TODO: add some error handling in form! (eg like in Zope)
336 uriFormat = [self uriAsFormat];
337 uri = [NSString stringWithFormat:uriFormat, @"view"];
339 r = [WOResponse responseWithRequest:req];
340 [r setStatus:302 /* moved */];
341 [r setHeader:uri forKey:@"location"];
345 @end /* UIxAppointmentEditor */