]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Scheduler/UIxAppointmentEditor.m
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@137 d1b88da0-ebda-0310-925b-ed51d...
[scalable-opengroupware.org] / SOGo / UI / Scheduler / UIxAppointmentEditor.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23
24 #include "common.h"
25 #include <SOGoUI/UIxComponent.h>
26 #include <SOGoLogic/SOGoAppointment.h>
27 #include <NGiCal/NGiCal.h>
28
29
30 /* TODO: CLEAN THIS MESS UP */
31
32 @interface NSDate(UsedPrivates)
33 - (NSString *)icalString; // TODO: this is in NGiCal
34 @end
35
36 @interface NSObject (AppointmentHack)
37 - (BOOL)isAppointment;
38 @end
39
40 @implementation NSObject (AppointmentHack)
41 - (BOOL)isAppointment {
42   return [self isKindOfClass:NSClassFromString(@"SOGoAppointmentObject")];
43 }
44 @end
45
46 @interface iCalPerson (Convenience)
47 - (NSString *)rfc822Email;
48 @end
49
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];
56     }
57     return _email;
58 }
59 @end
60
61 @interface UIxAppointmentEditor : UIxComponent
62 {
63     id appointment;
64     id participants;
65 }
66
67 - (SOGoAppointment *)appointment;
68 - (NSString *)iCalStringTemplate;
69 - (NSString *)iCalString;
70 - (BOOL)isNewAppointment;
71
72 @end
73
74 @implementation UIxAppointmentEditor
75
76 - (void)dealloc {
77     [self->appointment release];
78     [self->participants release];
79     [super dealloc];
80 }
81
82
83 /* accessors */
84
85
86 - (NSString *)formattedAptStartTime {
87     NSCalendarDate *date;
88     
89     date = [[self appointment] startDate];
90     /* TODO: convert this into display timeZone! */
91     return [date descriptionWithCalendarFormat:@"%A, %Y-%m-%d %H:%M %Z"];
92 }
93
94 - (BOOL)isNewAppointment {
95   /* that doesn't work! */
96   return ![[self clientObject] isAppointment];
97 }
98
99 - (NSString *)iCalString {
100   // TODO: improve check for new appointment
101   NSString *ical;
102
103   ical = [[self clientObject] valueForKey:@"iCalString"];
104   if ([ical length] == 0) /* a new appointment */
105     ical = [self iCalStringTemplate];
106   
107   return ical;
108 }
109
110 - (NSString *)iCalStringTemplate {
111   static NSString *iCalStringTemplate = \
112     @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
113     @"VERSION:2.0\n"
114     @"BEGIN:VEVENT\n"
115     @"UID:%@\n"
116     @"CLASS:PRIVATE\n"
117     @"STATUS:CONFIRMED\n"
118     @"DTSTAMP:%@\n"
119     @"DTSTART:%@\n"
120     @"DTEND:%@\n"
121     @"TRANSP:OPAQUE\n"
122     @"SEQUENCE:1\n"
123     @"END:VEVENT\n"
124     @"END:VCALENDAR";
125   NSCalendarDate *startDate, *endDate;
126   NSString       *template;
127   
128   startDate = [self selectedDate];
129   endDate   = [startDate dateByAddingYears:0 months:0 days:0
130                          hours:1 minutes:0 seconds:0];
131   
132   template = [NSString stringWithFormat:iCalStringTemplate,
133                          [[self clientObject] nameInContainer],
134                          [[NSCalendarDate date] icalString],
135                          [startDate icalString],
136                          [endDate   icalString]];
137   return template;
138 }
139
140
141 /* backend */
142
143
144 - (SOGoAppointment *)appointment {
145   if(self->appointment == nil) {
146     self->appointment = [[SOGoAppointment alloc]
147                           initWithICalString:[self iCalString]];
148   }
149   return self->appointment;
150 }
151
152 - (id)participants {
153   NSArray *attendees;
154   NSMutableArray *emailAddresses;
155   unsigned i, count;
156   
157   if (self->participants)
158     return self->participants;
159
160   attendees      = [self->appointment attendees];
161   count          = [attendees count];
162   emailAddresses = [[NSMutableArray alloc] initWithCapacity:count];
163   for (i = 0; i < count; i++) {
164     NSString *email;
165             
166     email = [[attendees objectAtIndex:i] rfc822Email];
167     if (email)
168       [emailAddresses addObject:email];
169   }
170   
171   self->participants = 
172     [[emailAddresses componentsJoinedByString:@"\n"] copy];
173   [emailAddresses release];
174   return self->participants;
175 }
176
177
178 /* helper */
179
180 - (NSString *)uriAsFormat {
181   NSString *uri, *qp;
182   NSRange r;
183
184   uri = [[[self context] request] uri];
185     
186   /* first: identify query parameters */
187   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
188   if (r.length > 0) {
189     uri = [uri substringToIndex:r.location];
190     qp = [uri substringFromIndex:r.location];
191   }
192   else
193     qp = nil;
194     
195   /* next: strip trailing slash */
196   if ([uri hasSuffix:@"/"])
197     uri = [uri substringToIndex:([uri length] - 1)];
198   r = [uri rangeOfString:@"/" options:NSBackwardsSearch];
199     
200   /* then: cut of last path component */
201   if (r.length == 0) // no slash? are we at root?
202     uri = @"/";
203   else
204     uri = [uri substringToIndex:(r.location + 1)];
205
206   /* next: append format token */
207   uri = [uri stringByAppendingString:@"%@"];
208   if(qp != nil)
209     uri = [uri stringByAppendingString:qp];
210   return uri;
211 }
212
213
214 /* new */
215
216
217 - (id)newAction {
218   /*
219     This method creates a unique ID and redirects to the "edit" method on the
220     new ID.
221     It is actually a folder method and should be defined on the folder.
222     
223     Note: 'clientObject' is the SOGoAppointmentFolder!
224   */
225   WORequest *req;
226   WOResponse *r;
227   NSString *uri, *uriFormat, *objectId, *nextMethod;
228   
229   objectId = [NSClassFromString(@"SOGoAppointmentFolder")
230                                globallyUniqueObjectId];
231   
232   nextMethod = [NSString stringWithFormat:@"%@/edit", objectId];
233   uriFormat  = [self uriAsFormat];
234   uri = [[NSString alloc] initWithFormat:uriFormat, nextMethod];
235   
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;
241   return r;
242 }
243
244
245 /* save */
246
247 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
248   _str = [_str stringByAppendingString:@" GMT"];
249   return [NSCalendarDate dateWithString:_str 
250                          calendarFormat:@"%Y-%m-%d %H:%M %Z"];
251 }
252
253 - (id)saveAction {
254   SOGoAppointment *apt;
255   NSString       *iCalString;
256   NSString       *summary, *location, *nextMethod, *uri, *uriFormat;
257   NSCalendarDate *sd, *ed;
258   NSArray        *ps;
259   unsigned       i, count;
260   WOResponse     *r;
261   WORequest      *req;
262
263   req = [[self context] request];
264
265   /* get iCalString from hidden input */
266   
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"];
272   }
273   
274   apt = [[SOGoAppointment alloc] initWithICalString:iCalString];
275   
276   /* merge in form values */
277   
278   sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
279   ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
280   [apt setStartDate:sd];
281   [apt setEndDate:ed];
282   
283   summary = [req formValueForKey:@"summary"];
284   [apt setSummary:summary];
285   location = [req formValueForKey:@"location"];
286   [apt setLocation:location];
287
288   [apt removeAllAttendees]; /* clean up */
289   ps = [[req formValueForKey:@"participants"]
290              componentsSeparatedByString:@"\n"];
291   count = [ps count];
292   for (i = 0; i < count; i++) {
293     NSString   *email;
294     iCalPerson *p;
295     NSRange    cnr;
296     
297     email = [ps objectAtIndex:i];
298     if ([email length] == 0)
299       continue;
300     
301     p = [[iCalPerson alloc] init];
302     [p setEmail:[@"mailto:" stringByAppendingString:[email stringValue]]];
303     /* construct a fake CN */
304     cnr = [email rangeOfString:@"@"];
305     if (cnr.length > 0)
306       [p setCn:[email substringToIndex:cnr.location]];
307     [apt addToAttendees:p];
308     [p release];
309   }
310   
311   /* receive current representation for save operation */
312   iCalString = [apt iCalString];
313   [apt release]; apt = nil;
314   
315   /* determine what's to do and where to go next */
316   if([self isNewAppointment])
317     nextMethod = @"duhduh";
318   else
319     nextMethod = @"view";
320   
321 #if 0
322   NSLog(@"%s new iCalString:\n%@", __PRETTY_FUNCTION__, iCalString);
323 #else
324   {
325     NSException *ex;
326     
327     ex = [[self clientObject] saveContentString:iCalString];
328     if (ex) return ex;
329     // TODO: add some error handling in form! (eg like in Zope)
330   }
331 #endif
332   
333   uriFormat = [self uriAsFormat];
334   uri = [NSString stringWithFormat:uriFormat, nextMethod];
335
336   r = [WOResponse responseWithRequest:req];
337   [r setStatus:302 /* moved */];
338   [r setHeader:uri forKey:@"location"];
339   return r;
340 }
341
342 @end /* UIxAppointmentEditor */