]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Scheduler/UIxAppointmentEditor.m
minor additions
[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
71 @end
72
73 @implementation UIxAppointmentEditor
74
75 - (void)dealloc {
76     [self->appointment release];
77     [self->participants release];
78     [super dealloc];
79 }
80
81
82 /* accessors */
83
84
85 - (NSString *)formattedAptStartTime {
86     NSCalendarDate *date;
87     
88     date = [[[[self appointment] startDate] copy] autorelease];
89     [date setTimeZone:[self viewTimeZone]];
90     return [date descriptionWithCalendarFormat:@"%A, %Y-%m-%d %H:%M %Z"];
91 }
92
93 - (BOOL)isNewAppointment {
94   /* that doesn't work! */
95   return ![[self clientObject] isAppointment];
96 }
97
98 - (NSString *)iCalString {
99   // TODO: improve check for new appointment
100   NSString *ical;
101
102   ical = [[self clientObject] valueForKey:@"iCalString"];
103   if ([ical length] == 0) /* a new appointment */
104     ical = [self iCalStringTemplate];
105   
106   return ical;
107 }
108
109 - (NSString *)iCalStringTemplate {
110   static NSString *iCalStringTemplate = \
111     @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
112     @"VERSION:2.0\n"
113     @"BEGIN:VEVENT\n"
114     @"UID:%@\n"
115     @"CLASS:PRIVATE\n"
116     @"STATUS:CONFIRMED\n"
117     @"DTSTAMP:%@\n"
118     @"DTSTART:%@\n"
119     @"DTEND:%@\n"
120     @"TRANSP:OPAQUE\n"
121     @"SEQUENCE:1\n"
122     @"END:VEVENT\n"
123     @"END:VCALENDAR";
124   NSCalendarDate *startDate, *endDate;
125   NSString       *template;
126   
127   startDate = [self selectedDate];
128   endDate   = [startDate dateByAddingYears:0 months:0 days:0
129                          hours:1 minutes:0 seconds:0];
130   
131   template = [NSString stringWithFormat:iCalStringTemplate,
132                          [[self clientObject] nameInContainer],
133                          [[NSCalendarDate date] icalString],
134                          [startDate icalString],
135                          [endDate   icalString]];
136   return template;
137 }
138
139
140 /* backend */
141
142
143 - (SOGoAppointment *)appointment {
144   if(self->appointment == nil) {
145     self->appointment = [[SOGoAppointment alloc]
146                           initWithICalString:[self iCalString]];
147   }
148   return self->appointment;
149 }
150
151 - (id)participants {
152   NSArray *attendees;
153   NSMutableArray *emailAddresses;
154   unsigned i, count;
155   
156   if (self->participants)
157     return self->participants;
158
159   attendees      = [self->appointment attendees];
160   count          = [attendees count];
161   emailAddresses = [[NSMutableArray alloc] initWithCapacity:count];
162   for (i = 0; i < count; i++) {
163     NSString *email;
164             
165     email = [[attendees objectAtIndex:i] rfc822Email];
166     if (email)
167       [emailAddresses addObject:email];
168   }
169   
170   self->participants = 
171     [[emailAddresses componentsJoinedByString:@"\n"] copy];
172   [emailAddresses release];
173   return self->participants;
174 }
175
176
177 /* helper */
178
179 - (NSString *)uriAsFormat {
180   NSString *uri, *qp;
181   NSRange r;
182
183   uri = [[[self context] request] uri];
184     
185   /* first: identify query parameters */
186   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
187   if (r.length > 0) {
188     uri = [uri substringToIndex:r.location];
189     qp = [uri substringFromIndex:r.location];
190   }
191   else
192     qp = nil;
193     
194   /* next: strip trailing slash */
195   if ([uri hasSuffix:@"/"])
196     uri = [uri substringToIndex:([uri length] - 1)];
197   r = [uri rangeOfString:@"/" options:NSBackwardsSearch];
198     
199   /* then: cut of last path component */
200   if (r.length == 0) // no slash? are we at root?
201     uri = @"/";
202   else
203     uri = [uri substringToIndex:(r.location + 1)];
204
205   /* next: append format token */
206   uri = [uri stringByAppendingString:@"%@"];
207   if(qp != nil)
208     uri = [uri stringByAppendingString:qp];
209   return uri;
210 }
211
212
213 /* new */
214
215
216 - (id)newAction {
217   /*
218     This method creates a unique ID and redirects to the "edit" method on the
219     new ID.
220     It is actually a folder method and should be defined on the folder.
221     
222     Note: 'clientObject' is the SOGoAppointmentFolder!
223   */
224   WORequest *req;
225   WOResponse *r;
226   NSString *uri, *uriFormat, *objectId, *nextMethod;
227   
228   objectId = [NSClassFromString(@"SOGoAppointmentFolder")
229                                globallyUniqueObjectId];
230   
231   nextMethod = [NSString stringWithFormat:@"%@/edit", objectId];
232   uriFormat  = [self uriAsFormat];
233   uri = [[NSString alloc] initWithFormat:uriFormat, nextMethod];
234   
235   req = [[self context] request];
236   r = [WOResponse responseWithRequest:req];
237   [r setStatus:302 /* moved */];
238   [r setHeader:uri forKey:@"location"];
239   [uri release]; uri = nil;
240   return r;
241 }
242
243
244 /* save */
245
246 /* returned dates are in GMT */
247 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
248     NSCalendarDate *date;
249
250     date = [NSCalendarDate dateWithString:_str 
251                            calendarFormat:@"%Y-%m-%d %H:%M %Z"];
252     [date setTimeZone:[self backendTimeZone]];
253     return date;
254 }
255
256 - (id)saveAction {
257   SOGoAppointment *apt;
258   NSString       *iCalString;
259   NSString       *summary, *location, *uri, *uriFormat;
260   NSCalendarDate *sd, *ed;
261   NSArray        *ps;
262   unsigned       i, count;
263   WOResponse     *r;
264   WORequest      *req;
265
266   req = [[self context] request];
267
268   /* get iCalString from hidden input */
269   
270   iCalString = [req formValueForKey:@"ical"];
271   if ([iCalString length] == 0) {
272     // TODO: improve user experience ... (eg error panel like Zope)
273     return [NSException exceptionWithHTTPStatus:400 /* bad request */
274                         reason:@"missing iCalendar form data in request"];
275   }
276   
277   apt = [[SOGoAppointment alloc] initWithICalString:iCalString];
278   
279   /* merge in form values */
280   
281   sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
282   ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
283   [apt setStartDate:sd];
284   [apt setEndDate:ed];
285   
286   summary = [req formValueForKey:@"summary"];
287   [apt setSummary:summary];
288   location = [req formValueForKey:@"location"];
289   [apt setLocation:location];
290
291   [apt removeAllAttendees]; /* clean up */
292   ps = [[req formValueForKey:@"participants"]
293              componentsSeparatedByString:@"\n"];
294   count = [ps count];
295   for (i = 0; i < count; i++) {
296     NSString   *email;
297     iCalPerson *p;
298     NSRange    cnr;
299     
300     email = [ps objectAtIndex:i];
301     if ([email length] == 0)
302       continue;
303     
304     p = [[iCalPerson alloc] init];
305     [p setEmail:[@"mailto:" stringByAppendingString:[email stringValue]]];
306     /* construct a fake CN */
307     cnr = [email rangeOfString:@"@"];
308     if (cnr.length > 0)
309       [p setCn:[email substringToIndex:cnr.location]];
310     [apt addToAttendees:p];
311     [p release];
312   }
313   
314   /* receive current representation for save operation */
315   iCalString = [apt iCalString];
316   [apt release]; apt = nil;
317     
318 #if 0
319   NSLog(@"%s new iCalString:\n%@", __PRETTY_FUNCTION__, iCalString);
320 #else
321   {
322     NSException *ex;
323     
324     ex = [[self clientObject] saveContentString:iCalString];
325     if (ex) return ex;
326     // TODO: add some error handling in form! (eg like in Zope)
327   }
328 #endif
329   
330   uriFormat = [self uriAsFormat];
331   uri = [NSString stringWithFormat:uriFormat, @"view"];
332
333   r = [WOResponse responseWithRequest:req];
334   [r setStatus:302 /* moved */];
335   [r setHeader:uri forKey:@"location"];
336   return r;
337 }
338
339 @end /* UIxAppointmentEditor */