]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Scheduler/UIxAppointmentEditor.m
8922837fde7b3bdd1010ccd30f531ffc98859570
[scalable-opengroupware.org] / SOGo / UI / Scheduler / UIxAppointmentEditor.m
1 /*
2   Copyright (C) 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 #include <SOGoUI/UIxComponent.h>
24
25 /* TODO: CLEAN UP */
26
27 @class NSString;
28 @class iCalPerson;
29 @class SOGoAppointment;
30
31 @interface UIxAppointmentEditor : UIxComponent
32 {
33   NSString *iCalString;
34   id appointment;
35   id item;
36   
37   /* individual values */
38   NSCalendarDate *startDate;
39   NSCalendarDate *endDate;
40   NSString       *title;
41   NSString       *location;
42   NSString       *comment;
43   NSArray        *participants;
44   NSArray        *resources;
45 }
46
47 - (SOGoAppointment *)appointment;
48 - (NSString *)iCalStringTemplate;
49 - (NSString *)iCalString;
50
51 - (NSString *)_completeURIForMethod:(NSString *)_method;
52
53 - (iCalPerson *)getOrganizer;
54 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
55   treatAsResource:(BOOL)_isResource;
56
57 @end
58
59 #include "common.h"
60 #include <SOGoUI/SOGoDateFormatter.h>
61 #include <SOGoLogic/SOGoAppointment.h>
62 #include <Appointments/SOGoAppointmentFolder.h>
63 #include <Appointments/SOGoAppointmentObject.h>
64 #include <NGiCal/NGiCal.h>
65 #include <SOGoLogic/AgenorUserManager.h>
66 #include "iCalPerson+UIx.h"
67 #include "UIxComponent+Agenor.h"
68
69 @interface NSDate(UsedPrivates)
70 - (NSString *)icalString; // TODO: this is in NGiCal
71 @end
72
73 @implementation UIxAppointmentEditor
74
75 - (void)dealloc {
76   [self->participants release];
77   [self->resources    release];
78   [self->startDate    release];
79   [self->endDate      release];
80   [self->title        release];
81   [self->location     release];
82   [self->comment      release];
83   [self->iCalString   release];
84   [self->appointment  release];
85   [self->item         release];
86   [super dealloc];
87 }
88
89 /* accessors */
90
91 - (void)setItem:(id)_item {
92   ASSIGN(self->item, _item);
93 }
94 - (id)item {
95   return self->item;
96 }
97
98 - (NSFormatter *)titleDateFormatter {
99   SOGoDateFormatter *fmt;
100   
101   fmt = [[[SOGoDateFormatter alloc] initWithLocale:[self locale]] autorelease];
102   [fmt setFullWeekdayNameAndDetails];
103   return fmt;
104 }
105
106 - (void)setAptStartDate:(NSCalendarDate *)_date {
107   ASSIGN(self->startDate, _date);
108 }
109 - (NSCalendarDate *)aptStartDate {
110   return self->startDate;
111 }
112 - (void)setAptEndDate:(NSCalendarDate *)_date {
113   ASSIGN(self->endDate, _date);
114 }
115 - (NSCalendarDate *)aptEndDate {
116   return self->endDate;
117 }
118
119 - (void)setTitle:(NSString *)_value {
120   ASSIGNCOPY(self->title, _value);
121 }
122 - (NSString *)title {
123   return self->title;
124 }
125 - (void)setLocation:(NSString *)_value {
126   ASSIGNCOPY(self->location, _value);
127 }
128 - (NSString *)location {
129   return self->location;
130 }
131 - (void)setComment:(NSString *)_value {
132   ASSIGNCOPY(self->comment, _value);
133 }
134 - (NSString *)comment {
135   return self->comment;
136 }
137
138 - (void)setParticipants:(NSArray *)_parts {
139   ASSIGN(self->participants, _parts);
140 }
141 - (NSArray *)participants {
142   return self->participants;
143 }
144 - (void)setResources:(NSArray *)_res {
145   ASSIGN(self->resources, _res);
146 }
147 - (NSArray *)resources {
148   return self->resources;
149 }
150
151 /* iCal */
152
153 - (void)setICalString:(NSString *)_s {
154   ASSIGNCOPY(self->iCalString, _s);
155 }
156 - (NSString *)iCalString {
157   return self->iCalString;
158 }
159
160 - (NSString *)iCalStringTemplate {
161   static NSString *iCalStringTemplate = \
162     @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
163     @"VERSION:2.0\n"
164     @"BEGIN:VEVENT\n"
165     @"UID:%@\n"
166     @"CLASS:PRIVATE\n"
167     @"STATUS:CONFIRMED\n"
168     @"DTSTAMP:%@\n"
169     @"DTSTART:%@\n"
170     @"DTEND:%@\n"
171     @"TRANSP:OPAQUE\n"
172     @"SEQUENCE:1\n"
173     @"END:VEVENT\n"
174     @"END:VCALENDAR";
175   NSCalendarDate *lStartDate, *lEndDate;
176   NSString       *template;
177   
178   lStartDate = [self selectedDate];
179   lEndDate   = [lStartDate dateByAddingYears:0 months:0 days:0
180                          hours:1 minutes:0 seconds:0];
181   
182   template = [NSString stringWithFormat:iCalStringTemplate,
183                          [[self clientObject] nameInContainer],
184                          [[NSCalendarDate date] icalString],
185                          [lStartDate icalString],
186                          [lEndDate   icalString]];
187   return template;
188 }
189
190 - (SOGoAppointment *)appointment {
191   return self->appointment;
192 }
193
194 /* helper */
195
196 - (NSString *)_completeURIForMethod:(NSString *)_method {
197   NSString *uri;
198   NSRange r;
199     
200   uri = [[[self context] request] uri];
201     
202   /* first: identify query parameters */
203   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
204   if (r.length > 0)
205     uri = [uri substringToIndex:r.location];
206     
207   /* next: append trailing slash */
208   if (![uri hasSuffix:@"/"])
209     uri = [uri stringByAppendingString:@"/"];
210   
211   /* next: append method */
212   uri = [uri stringByAppendingString:_method];
213     
214   /* next: append query parameters */
215   return [self completeHrefForMethod:uri];
216 }
217
218 /* new */
219
220 - (id)newAction {
221   /*
222     This method creates a unique ID and redirects to the "edit" method on the
223     new ID.
224     It is actually a folder method and should be defined on the folder.
225     
226     Note: 'clientObject' is the SOGoAppointmentFolder!
227           Update: remember that there are group folders as well.
228   */
229   NSString *uri, *objectId, *nextMethod;
230   
231   objectId = [NSClassFromString(@"SOGoAppointmentFolder")
232                                globallyUniqueObjectId];
233   if ([objectId length] == 0) {
234     return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
235                         reason:@"could not create a unique ID"];
236   }
237   
238   nextMethod = [NSString stringWithFormat:@"../%@/edit", objectId];
239   uri = [self _completeURIForMethod:nextMethod];
240   return [self redirectToLocation:uri];
241 }
242
243 /* save */
244
245 /* returned dates are in GMT */
246 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
247   NSCalendarDate *date;
248   
249   date = [NSCalendarDate dateWithString:_str 
250                          calendarFormat:@"%Y-%m-%d %H:%M %Z"];
251   [date setTimeZone:[self backendTimeZone]];
252   return date;
253 }
254
255 - (iCalPerson *)getOrganizer {
256   iCalPerson *p;
257   NSString   *emailProp;
258   
259   emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
260   p = [[[iCalPerson alloc] init] autorelease];
261   [p setEmail:emailProp];
262   [p setCn:[self cnForUser]];
263   return p;
264 }
265
266 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
267   treatAsResource:(BOOL)_isResource
268 {
269   unsigned i, count;
270   NSMutableArray *result;
271
272   count = [_values count];
273   result = [[NSMutableArray alloc] initWithCapacity:count];
274   for (i = 0; i < count; i++) {
275     NSString   *pString, *email, *cn;
276     NSRange    r;
277     iCalPerson *p;
278     
279     pString = [_values objectAtIndex:i];
280     if ([pString length] == 0)
281       continue;
282     
283     /* delimiter between email and cn */
284     r = [pString rangeOfString:@";"];
285     if (r.length > 0) {
286       email = [pString substringToIndex:r.location];
287       cn = (r.location + 1 < [pString length])
288         ? [pString substringFromIndex:r.location + 1]
289         : nil;
290     }
291     else {
292       email = pString;
293       cn    = nil;
294     }
295     if (cn == nil) {
296       /* fallback */
297       AgenorUserManager *um = [AgenorUserManager sharedUserManager];
298       cn = [um getCNForUID:[um getUIDForEmail:email]];
299     }
300     
301     p = [[iCalPerson alloc] init];
302     [p setEmail:[@"mailto:" stringByAppendingString:email]];
303     if ([cn isNotNull]) [p setCn:cn];
304     
305     /* see RFC2445, sect. 4.2.16 for details */
306     [p setRole:_isResource ? @"NON-PARTICIPANT" : @"REQ-PARTICIPANT"];
307     [result addObject:p];
308     [p release];
309   }
310   return [result autorelease];
311 }
312
313 - (BOOL)isWriteableClientObject {
314   return [[self clientObject] 
315                 respondsToSelector:@selector(saveContentString:)];
316 }
317
318 - (void)loadValuesFromAppointment:(SOGoAppointment *)_appointment {
319   self->startDate = [[_appointment startDate] copy];
320   self->endDate   = [[_appointment endDate]   copy];
321   [self->startDate setTimeZone:[self viewTimeZone]];
322   [self->endDate   setTimeZone:[self viewTimeZone]];
323   
324   self->title    = [[_appointment summary]  copy];
325   self->location = [[_appointment location] copy];
326   self->comment  = [[_appointment comment]  copy];
327   
328   self->participants = [[_appointment participants] retain];
329   self->resources    = [[_appointment resources]    retain];
330 }
331
332 /* actions */
333
334 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
335   return YES;
336 }
337
338 - (id)testAction {
339   /* for testing only */
340   WORequest *req;
341     
342   NSLog(@"%s BEEN HERE!", __PRETTY_FUNCTION__);
343
344   req = [[self context] request];
345   NSLog(@"%@", [req formValues]);
346   return self;
347 }
348
349 - (id)defaultAction {
350   NSString *ical;
351   
352   /* load iCalendar file */
353   
354   ical = [[self clientObject] valueForKey:@"iCalString"];
355   if ([ical length] == 0) /* a new appointment */
356     ical = [self iCalStringTemplate];
357   
358   [self setICalString:ical];
359   
360   /* parse */
361   
362   self->appointment = 
363     [[SOGoAppointment alloc] initWithICalString:[self iCalString]];
364   
365   /* load values */
366   
367   [self loadValuesFromAppointment:[self appointment]];
368   
369   return self;
370 }
371
372 - (id)saveAction {
373   NSString       *uri;
374   NSCalendarDate *sd, *ed;
375   NSArray        *attendees, *lResources;
376   WORequest      *req;
377   
378   if (![self isWriteableClientObject]) {
379     /* return 400 == Bad Request */
380     return [NSException exceptionWithHTTPStatus:400
381                         reason:@"method cannot be invoked on "
382                                @"the specified object"];
383   }
384   
385   req = [[self context] request];
386   
387   /* get iCalString from hidden input */
388   [self setICalString:[req formValueForKey:@"ical"]];
389
390   self->appointment = 
391     [[SOGoAppointment alloc] initWithICalString:[self iCalString]];
392   
393   if (self->appointment == nil) {
394     return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
395                         reason:@"invalid iCalendar form data in request"];
396   }
397   
398   /* merge in form values */
399   
400   sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
401   ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
402   [self->appointment setStartDate:sd];
403   [self->appointment setEndDate:ed];
404   
405   [self->appointment setSummary:[req formValueForKey:@"summary"]];
406   [self->appointment setLocation:[req formValueForKey:@"location"]];
407   
408   attendees = [self getICalPersonsFromFormValues:
409                       [req formValuesForKey:@"participants"]
410                     treatAsResource:NO];
411   lResources = [self getICalPersonsFromFormValues:
412                       [req formValuesForKey:@"resources"]
413                     treatAsResource:YES];
414   if ([lResources count] > 0) {
415     attendees = ([attendees count] > 0)
416       ? [attendees arrayByAddingObjectsFromArray:lResources]
417       : lResources;
418   }
419   [self->appointment setAttendees:attendees];
420   
421   [self->appointment setOrganizer:[self getOrganizer]];
422   
423   /* receive current representation for save operation */
424   {
425     NSException *ex;
426     NSString *content;
427     
428     content = [self->appointment iCalString];
429     if (content == nil) {
430       return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
431                           reason:@"failed to create iCal content for apt"];
432     }
433     
434     ex = [[self clientObject] saveContentString:content];
435     if (ex) return ex;
436     // TODO: add some error handling in form! (eg like in Zope)
437   }
438   
439   uri = [self _completeURIForMethod:@".."];
440   return [self redirectToLocation:uri];
441 }
442
443 @end /* UIxAppointmentEditor */